Категория > Новости > Вызов мастеру ключей. Инжектим шелл-код в память KeePass, обойдя антивирус - «Новости»
Вызов мастеру ключей. Инжектим шелл-код в память KeePass, обойдя антивирус - «Новости»2-04-2022, 00:00. Автор: Owen |
keepass2john.py. В KeePass — доступы к критически важным ресурсам инфры, определяющим исход внутряка, поэтому добраться до нее нужно. На рабочей станции, где пользак крутит интересующую нас базу, глядит в оба Kaspersky Endpoint Security (он же KES), который не дает расслабиться. Рассмотрим, какие есть варианты получить желанный мастер‑пароль, не прибегая к социнженерии. Прежде всего скажу, что успех этого предприятия — в обязательном использовании крутой малвари KeeThief из коллекции GhostPack авторства небезызвестных @harmj0y и @tifkin_. Ядро программы — кастомный шелл‑код, который вызывает RtlDecryptMemory в отношении зашифрованной области виртуальной памяти KeePass.exe и выдергивает оттуда наш мастер‑пароль. Если есть шелл‑код, нужен и загрузчик, и с этим возникают трудности, когда на хосте присутствует EDR... Впрочем, мы отвлеклись. Какие были варианты? Потушить AVСамый простой (и глупый) способ — вырубить к чертям «Касперского» на пару секунд. «Это не редтим, поэтому право имею!» — подумал я. Так как привилегии администратора домена есть, есть и доступ к серверу администрирования KES. Следовательно, и к учетке Порядок действий простой. Дампаю LSA с помощью secretsdump.py. Потрошим LSA Гружу консоль администрирования KES с официального сайта и логинюсь, указав хостнейм KSC. Консоль администрирования KES Стопорю «Каспера» и делаю свои грязные делишки. AdobeHelperAgent.exe, ну вы поняли, ага Profit! Мастер‑пароль у нас. После окончания проекта я опробовал другие способы решить эту задачу. Получить сессию C2Многие C2-фреймворки умеют тащить за собой DLL рантайма кода C# (Common Language Runtime, CLR) и загружать ее отраженно по принципу RDI (Reflective DLL Injection) для запуска малвари из памяти. Теоретически это может повлиять на процесс отлова управляемого кода, исполняемого через такой трюк. Полноценную сессию Meterpreter при активном антивирусе Касперского получить трудно из‑за обилия артефактов в сетевом трафике, поэтому его execute-assembly я даже пробовать не стал. А вот модуль execute-assembly Cobalt Strike принес свои результаты, если правильно получить сессию beacon (далее скриншоты будут с домашнего KIS, а не KES, но все техники работают и против последнего — проверено). KeeTheft.exe с помощью execute-assembly CS Все козыри раскрывать не буду — мне еще работать пентестером, однако этот метод тоже не представляет большого интереса в нашей ситуации. Для гладкого получения сессии «маячка» нужен внешний сервак, на который надо накрутить валидный сертификат для шифрования SSL-трафика, а заражать таким образом машину с внутреннего периметра заказчика — совсем невежливо. Перепаять инструментСамый интересный и в то же время трудозатратный способ — переписать логику инъекции шелл‑кода таким образом, чтобы EDR не спалил в момент исполнения. Это то, ради чего мы сегодня собрались, но для начала немного теории. ПримечаниеДело здесь именно в уклонении от эвристического анализа, так как, если спрятать сигнатуру малвари с помощью недетектируемого упаковщика, доступ к памяти нам все равно будет запрещен из‑за фейла инъекции. Запуск криптованного KeeTheft.exe при активном EDR Классическая инъекция шелл-кодаОглянемся назад и рассмотрим классическую технику внедрения стороннего кода в удаленный процесс. Для этого наши предки пользовались священным трио Win32 API:
Исполнение шелл‑кода с помощью Thread Execution (изображение — elastic.co) Напишем простой PoC на C#, демонстрирующий эту самую классическую инъекцию шелл‑кода.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace SimpleInjector
{
public class Program
{
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern IntPtr OpenProcess(
uint processAccess,
bool bInheritHandle,
int processId);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern IntPtr VirtualAllocEx(
IntPtr hProcess,
IntPtr lpAddress,
uint dwSize,
uint flAllocationType,
uint flProtect);
[DllImport("kernel32.dll")]
static extern bool WriteProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
byte[] lpBuffer,
Int32 nSize,
out IntPtr lpNumberOfBytesWritten);
[DllImport("kernel32.dll")]
static extern IntPtr CreateRemoteThread(
IntPtr hProcess,
IntPtr lpThreadAttributes,
uint dwStackSize,
IntPtr lpStartAddress,
IntPtr lpParameter,
uint dwCreationFlags,
IntPtr lpThreadId);
public static void Main()
{
// msfvenom -p windows/x64/messagebox TITLE='MSF' TEXT='Hack the Planet!' EXITFUNC=thread -f csharp
byte[] buf = new byte[] { };
// Получаем PID процесса explorer.exe
int processId = Process.GetProcessesByName("explorer")[0].Id;
// Получаем хендл процесса по его PID (0x001F0FFF = PROCESS_ALL_ACCESS)
IntPtr hProcess = OpenProcess(0x001F0FFF, false, processId);
// Выделяем область памяти 0x1000 байт (0x3000 = MEM_COMMIT | MEM_RESERVE, 0x40 = PAGE_EXECUTE_READWRITE)
IntPtr allocAddr = VirtualAllocEx(hProcess, IntPtr.Zero, 0x1000, 0x3000, 0x40);
// Записываем шелл-код в выделенную область
_ = WriteProcessMemory(hProcess, allocAddr, buf, buf.Length, out _);
// Запускаем поток
_ = CreateRemoteThread(hProcess, IntPtr.Zero, 0, allocAddr, IntPtr.Zero, 0, IntPtr.Zero);
}
}
}
Скомпилировав и запустив инжектор, с помощью Process Hacker можно наблюдать, как в процессе explorer.exe запустится новый поток, рисующий нам диалоговое окно MSF. Классическая инъекция шелл‑кода Если просто положить такой бинарь на диск с активным средством антивирусной защиты, реакция будет незамедлительной независимо от содержимого массива VirusTotal намекает... Как антивирусное ПО понимает, что перед ним инжектор, даже без динамического анализа? Все просто: пачка атрибутов ПримечаниеЗдесь используется сборка $stream=[System.IO.File]::OpenRead($assembly)$peReader=[System.Reflection.PortableExecutable.PEReader]::new($stream,[System.Reflection.PortableExecutable.PEStreamOptions]::LeaveOpen-bor[System.Reflection.PortableExecutable.PEStreamOptions]::PrefetchMetadata)$metadataReader=[System.Reflection.Metadata.PEReaderExtensions]::GetMetadataReader($peReader)$assemblyDefinition=$metadataReader.GetAssemblyDefinition()foreach($typeHandlerin$metadataReader.TypeDefinitions){$typeDef=$metadataReader.GetTypeDefinition($typeHandler)foreach($methodHandlerin$typeDef.GetMethods()){$methodDef=$metadataReader.GetMethodDefinition($methodHandler)$import=$methodDef.GetImport()if($import.Module.IsNil){continue}$dllImportFuncName=$metadataReader.GetString($import.Name)$dllImportParameters=$import.Attributes.ToString()$dllImportPath=$metadataReader.GetString($metadataReader.GetModuleReference($import.Module).Name)Write-Host"$dllImportPath,$dllImportParameters`n$dllImportFuncName`n"}} Смотрим импорты в SimpleInjector.exeinfoЭти импорты представляют собой способ взаимодействия приложений .NET с неуправляемым кодом — таким, например, как функции библиотек При анализе этого добра в динамике, как ты понимаешь, дела обстоят еще проще: так как все EDR имеют привычку вешать хуки на userland-интерфейсы, вызовы подозрительных API сразу поднимут тревогу. Подробнее об этом можно почитать в ресерче @ShitSecure, а в лабораторных условиях хукинг нагляднее всего продемонстрировать с помощью API Monitor. Хукаем kernel32.dll в SimpleInjector.exe Итак, что же со всем этим делать? Введение в D/InvokeВ 2020 году исследователи @TheWover и @FuzzySecurity представили новый API для вызова неуправляемого кода из .NET — D/Invoke (Dynamic Invocation, по аналогии с P/Invoke). Этот способ основан на использовании мощного механизма делегатов в C# и изначально был доступен как часть фреймворка для разработки постэксплуатационных тулз SharpSploit, однако позже был вынесен в отдельный репозиторий и даже появился в виде сборки на NuGet. С помощью делегатов разработчик может объявить ссылку на функцию, которую хочет вызвать, со всеми параметрами и типом возвращаемого значения, как и при использовании импорта с помощью атрибута D/Invoke предоставляет не один подход для динамического импорта неуправляемого кода, в том числе:
Чтобы было понятнее, разберем для начала простой пример, который делает нечто похожее на первый подход, но без использования D/Invoke. DynamicAPIInvoke без D/InvokeМне очень нравится пример из статьи xpn (второй листинг кода в разделе «A Quick History Lesson»), где он показывает, как можно использовать всю мощь делегатов вместе с ручным поиском экспорт‑адреса неуправляемой функции менее чем за 50 строк. Перейти обратно к новости |