Внутри Windows кроется огромное количество интересных и неочевидных возможностей. В этой статье я покажу, как заставить операционку загрузить нашу библиотеку в любой процесс!
Одна из самых популярных атак, направленных на повышение привилегий, — это DLL Hijacking. Чтобы ее провести, атакующий помещает свою вредоносную библиотеку на пути поиска легитимной DLL. Это приводит к тому, что целевое приложение подгружает стороннюю либу и выполняет вредоносный код.
На первый взгляд такая атака кажется очень простой. Я бы даже сказал примитивной. Тем не менее, существует несколько подводных камней, которые часто упускают из вида атакующие.
Во?первых, многие забывают сделать DLL Proxying до целевой библиотеки, что приводит к поломке всего приложения. Оно крашится, так как пытается вызвать функцию из библиотеки, в которой нужного кода нет.
Во?вторых, иногда вызов функций вроде LoadLibrary(), CreateProcess() и CreateThread() помещают в функцию DllMain(), что приводит к дедлоку (Dead Lock) из?за механизма Loader Lock. Loader Lock выступает в качестве критической секции (примитив синхронизации потоков процесса). Фактически выполнение потока программы блокируется до момента снятия Loader Lock.
В?третьих, существуют некоторые факторы, влияющие на порядок поиска DLL. Стандартные пути поиска изображены ниже.
Это так называемый SafeDllSearchOrder. Если он отключен, то после Application Directory функция LoadLibrary*() смотрит Current Directory. Отключить SafeDllSearchMode можно, выставив в ноль значение по этому пути:
Еще один фактор, влияющий на поиск — это функция LoadLibraryEx(), вызванная со значением LOAD_WITH_ALTERED_SEARCH_PATH. В таком случае первым делом DLL ищутся по пути, указанному внутри этой функции.
Помимо прочего существуют встроенные механизмы Windows, которые позволяют внедрить нашу библиотеку в целевой процесс. В документации Microsoft есть упоминание некоторых из них. Давай изучим их подробнее.
DLL REDIRECTION
Для обычных исполняемых файлов
DLL Redirection — специальный механизм, позволяющий программам использовать разные версии DLL для своих задач, причем не затрагивая обычные системные библиотеки. Действие распространяется только на функции LoadLibrary*().
Фактически, независимо от того, указан ли в ней полный путь (C:WindowsSystem32dll.dll) или короткий (dll.dll), функция проверит, присутствует ли в текущей директории (в которой находится приложение, которое вызвало эту функцию) файл с расширением .local. И если присутствует, то функция LoadLibrary*() в любом случае загрузит в первую очередь DLL из текущей директории приложения.
Название файла .local должно быть таким же, как и название процесса, из которого вызвана функция LoadLibrary(). Например, если приложение — Editor.exe, то имя файла должно быть Editor.exe.local.
Представим, что этот самый C:myappEditor.exe попытается через LoadLibrary*() загрузить какую?нибудь либу. Например, такую:
C:Program FilesCommon FilesSystemmydll.dll
Тогда LoadLibrary*() проверит существование файла Editor.exe.local в директории, где лежит Editor.exe. Если файл .local найдется, то функция попытается сначала загрузить mydll.dll из текущей директории.
То есть сначала проверяется этот путь:
C:myappmydll.dll
И если такого файла нет, то загрузится по указанному полному пути:
C:Program FilesCommon FilesSystemmydll.dll
Самое интересное: мы можем создать не только файл Editor.exe.local, но и папку с таким названием, потому что содержимое файла .local не проверяется. В таком случае DLL будет подгружена по следующему пути:
C:myappmyapp.exe.localmydll.dll
Итак, приступим к написанию PoC. Во?первых, нам нужно целевое приложение, которое будет подгружать библиотеку с указанием полного пути.
В качестве легитимной библиотеки скомпилируем следующий код и назовем Redir.dll.
#include "pch.h"BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ){ switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: MessageBox(NULL, L"HI FROM LEGIT", L"HI FROM LEGIT", MB_OK); case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE;}
После компиляции перенесем в папку C:UsersMichaelDesktop библиотеку Redir.dll. Проверим, что она успешно запускается и выполняется.
Теперь изменим код Redir.dll на следующий.
#include "pch.h"BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ){ switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: MessageBox(NULL, L"HI FROM FAKE", L"HI FROM FAKE", MB_OK); case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE;}
Скомпилируем его и создадим в папке с Article.exe файл Article.exe.local.
Теперь запустим исполняемый файл и убедимся, что библиотека действительно загружается из текущей директории приложения, а не по полному пути.
Если удалить файл .local, то вновь будет загружаться нужная библиотека.
Сборки .NET
Для сборок .NET все чуточку проще. Нам нужно создать файл .manifest, либо отредактировать существующий, добавив в него зависимость от конкретной библиотеки. Вот пример конфигурационного файла.
В данном случае мы с помощью атрибута name указываем, что целевая сборка зависит от user32.dll. После чего файл нужно сохранить с названием program.exe.manifest, где program.exe — имя приложения, в которое должна подгрузиться библиотека.
Это приведет к тому, что user32.dll будет подгружаться из той директории, откуда запускается приложение.
IMAGE PATH NAME SPOOFING
Теория
Атака заключается в том, что мы можем использовать функции Rtl* и обмануть приложение, заставив его думать, что оно запускается из другой директории. Образно, приложение лежит в C:WindowsSystem32abc.exe, а мы скажем, что в C:Usersabc.exe. Как следствие, abc.exe будет грузить библиотеки из C:Users.
Все основано на функциях RtlCreateProcessParametersEx() и RtlCreateUserProcess(). Windows (а также платформа CLR) будет искать библиотеки (либо сборки .NET, в случае CLR) по пути, указанном в элементе ImagePathName структуры RTL_USER_PROCESS_PARAMETERS. Эту структуру генерирует функция RtlCreateProcessParametersEx(). Запущенный процесс в свою очередь будет парсить эту структуру и извлечет из нее ImagePathName. И, как следствие, раскроет текущую директорию, которая в действительности спуфнута.
Реализация
Функция RtlCreateProcessParametersEx() выглядит вот так.
Эта атака не сработает, если приложение подгружает целевую библиотеку с указанием полного пути. Я пытался совместить эту атаку с DLL Redirection с созданием файла .local, но безуспешно.
Вернемся к нашему эксперименту. Чуть?чуть поправим файл Article.exe, чтобы он загружал библиотеку без указания полного пути.
В соответствии с порядком поиска DLL Windows в первую очередь будет пытаться найти Redir.dll в текущей директории приложения. Здесь?то мы его и поймаем!
Убедимся в работоспособности приложения.
Теперь удаляем Article.exe из папки с фейковой DLL и начинаем писать загрузчик. Назовем его PathSpoof.exe.
Я не смог найти ссылку на gists, но этот код я когда?то стащил у уважаемого snovvcrash. Предлагаю только несколько поправить исходник, изменив с учетом наших целей.
Запускаем PathSpoof.exe и видим успешную подгрузку библиотеки.
WINSXS
Механизм WinSxS (Windows Side By Side) служит для хранения разных версий важных системных файлов. После обновления Windows в папку C:WindowsWinSxS падают прошлые версии всяких программных компонентов. Это позволяет в случае сбоя откатиться назад и вернуть систему к жизни.
Исследователи из SecurityJoes обнаружили, что в папку WinSxS попадают ехе?приложения, уязвимые к атаке DLL Hijacking. Дело в том, что порядок поиска библиотек у этих приложений следующий:
папка, в которой лежит .ехе;
C:WindowsSystem32;
C:WindowsSystem;
C:Windows;
текущая папка.
И именно на пятом шаге исследователи ловили приложения из WinSxS, пытающиеся подгрузить библиотеку из их текущей директории. Алгоритм обнаружения донельзя прост:
запускаем cmd.exe;
заходим в C:Users<currentuser>Desktop;
запускаем приложение WinSxS.
Если приложение уязвимо, то оно порыскает в папках из 1-4 шагов, а потом придет в C:Users<currentuser>Desktop, чтобы найти целевую библиотеку. Здесь?то с помощью Process Monitor его и поймают!
В ходе ресерча нашлось множество уязвимых приложений.
Ты можешь и сам искать подобные дырки, используя тот же Process Monitor или DLLHSC.
К слову, в ходе одного из проектов по пентесту мне удалось подобным образом проэксплуатировать WinSxS, но там сработало несколько условий:
целевое приложение было сборкой .NET;
нужно было реализовать MITRE T1574 Hijack Execution Flow.
Поэтому я прибегнул к тактике с AppDomain Manager Injection.
Этот файл лежал рядом с NetConfigLdr.exe, равно как и библиотека AppDomInject.dll. Это позволило реализовать MITRE и проэксплуатировать файл из WinSxS.
Кстати, если ты еще не забыл про Image Path Name Spoofing, то AppDomain Injection получится совмещать с таким спуфингом. Это подробно описано в исследовании у Sektor7.
SVCHOST.EXE
Отдельно я хочу упомянуть инжект в svchost.exe (то есть внедрение в любую службу). Сам по себе svchost.exe — один из множества служебных процессов. Он может подгружать DLL-файл службы, взяв путь из записи реестра со значением ServiceDll.
Например, для службы TermSrv есть файл termsrv.dll, он находится в %SystemRoot%System32. Этот путь прописан внутри значения ServiceDll вот здесь:
Причем есть даже PoC, позволяющий хукнуть функцию SpAcceptCredentials() и извлекать учетные данные пользователей.
ВЫВОДЫ
У Windows есть необычные возможности, которым иногда пользуются и атакующие. Конечно, порой проще нагло влезть в адресное пространство процесса, записать туда байты DLL-библиотеки и дернуть CreateRemoteThread(), но это далеко не панацея. В конце концов, знаешь больше способов — крепче спишь (и быстрее закрываешь проекты)!