BlackLotus UEFI bootkit. Часть 1

Добро пожаловать на наш форум!

Спасибо за посещение нашего сообщества. Пожалуйста, зарегистрируйтесь или войдите, чтобы получить доступ ко всем функциям.


Gibby

Автор
Команда проекта

Регистрация
Сообщений
1,635
Репутация
45
Сделок
Приветствую вас, дорогие читатели! Сегодня я хочу поделиться с вами своим опытом изучения BlackLotus UEFI bootkit. В этом исследование разберем следующие темы:

  1. Подготовка тестового стенда.
  2. Запуск CVE-2022–21 894 (baton drop).
  3. Компиляция payload и компонентов для его выполнения.
  4. Добавление сертификата в базу данных MOK.
  5. Чтение и запись файлов в операционной системе Windows10 из файловой системы NTFS через grub.elf.
Давайте углубимся в эти интересные темы и разберемся, как функционирует одна из современных угроз безопасности.

1. Подготовка тестового стенда.​


1.png

Состав стенда:

  1. Windows 10×64 — рабочее место исследователя.
  2. VMware Workstation — платформа для виртуализации (Версия 17.0.0 build-20 800 274).
  3. VMware Workstation Менеджер — ПО для предоставления доступа к виртуальным машинам на VMware Workstation.
  4. Windows 10×64-BatonDrop — виртуальная машина, подготовленная для проведения исследований (Версия Windows 10 Pro 21H2 19 044.1288). Настройки системы были выбраны стандартные.

2.png

1.1 Попытка запуска CVE-2022-21894(BatonDrop).​

Для начала попробуем запустить CVE-2022-21894. В этом исследование действия будут проводиться с файлом poc_amd64_19041.iso, который скачаем по ссылке.

3.png

Для начала необходимо примонтировать «Системный раздел EFI», для этого необходимо проделать следующие шаги.

4.png

5.png

Далее нужно скопировать файлы из poc_amd64_19041.iso в системный раздел. Для используем Total Commander (Total Commander нужно запускать с правами администратора).

6.png

Сделаем копию оригинального BCD файла и назовём ее BCDR(Понадобиться потом).

7.png

Создадим snapshot для виртуальной машины Windows 10 x64-BatonDrop, так как в будущем потребуется к нему вернуться.

8.png

Импортируем bcd файл из poc_amd64_19041.iso с помощью bcdedit.exe.

9.png

После этого выключаем виртуальную машину Windows 10 x64-BatonDrop и при включении возникает ошибка.

10.png

2. Отладка CVE-2022-21894 (baton drop)​

После появления ошибки 0xc0 000 010, обратимся к статье BlackLotus UEFI bootkit: Myth confirmed (welivesecurity.com), чтобы понять, какие файлы загружаются и в каком порядке. Эта информация важна для определения того, что именно требуется отладить. Только пройдя по этим файлам можно понять, где срабатывает ошибка 0xc0 000 010.

11.png

Замечание: При отладке Windows 10x64-BatonDrop необходимо использовать одно ядро.

12.png

2.1 Настройка отладки​

Для начала разберёмся, что именно мы будем настраивать и как это сделать. Мы изучили документы MSDN по следующим темам:

Теперь приступим к настройке отладки в системе Windows 10 x64-BatonDrop. Возвращаемся к snapshot good.

13.png

Выполняем команды от имени администратора в виртуальной машине Windows 10x64-BatonDrop, затем выключим виртуальную машину Windows 10x64-BatonDrop.

14.png

Добавляем Serial Port для Windows 10x64-BatonDrop.

15.png

Потом изменим файл C:\Users\user\Documents\Virtual Machines\Windows 10x64-BatonDrop\Windows 10x64-BatonDrop.vmx. Удалим строки.

16.png

Переименуем serial1 на serial0 и сохраняем файл Windows 10x64-BatonDrop.vmx.

17.png

Находясь на рабочем месте исследователя Windows 10x64, настроим Windbg.

18.png

2.2 Отладка файла bootmgfw.elf.​

После настройке отладки запускаем виртуальную машину Windows 10x64-BatonDrop и в Windbg(Windows 10x64) видим подключение.

19.png

Дальше срабатывает системное прерывание int 3.

20.png

После срабатывания системного прерывания ищем загруженные файлы.

21.png

У нас есть файл bootmgfw (с ним и начнем работать), у которого Imagebase 0x0000000010000000.

22.png

Теперь возвращаемся к snapshot good и импортируем bcd file.

23.png

Выключаем виртуальную машину Windows 10x64-BatonDrop и изменяем файл C:\Users\user\Documents\Virtual Machines\Windows 10x64-BatonDrop\Windows 10x64-BatonDrop.vmx, чтобы отладить с помощью IDA Pro.

24.png

Включаем виртуальную машину Windows 10x64-BatonDrop и смотрим файл C:\Users\user\Documents\Virtual Machines\Windows 10x64-BatonDrop\vmware.log, так как нам необходимо узнать порт для подключения с помощью IDA Pro.

25.png

После проделанных действий открываем IDA Pro и загружаем файл bootmgfw.efi(Файл bootmgfw.efi берётся из E:\EFI\Microsoft\Boot\bootmgfw.efi).

26.png


Добавляем bootmgfw.pdb файл в IDA PRO.

27.png

Замечание: Файл bootmgfw.pdb можно скачать, воспользовавшись ссылкой http://msdl.microsoft.com/download/...94B898929165E26611E4791B87F6B1B2/bootmgfw.pdb, где C94B898929165E26611E4791B87F6B1B2.

28.png

Или можно использовать скрипт pdbdownload.py.

Python:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
import os
import re
import binascii
import pefile
import struct

def to_pdb(filename):
    return re.sub(r'.[^.]+$', '.pdb', os.path.basename(filename))

def build_url(filename):
    guid = ""
    pdb = to_pdb(filename)
    pe = pefile.PE(filename)

    for dbg in pe.DIRECTORY_ENTRY_DEBUG:
        if dbg.struct.Type == 2:  # IMAGE_DEBUG_TYPE_CODEVIEW
            guid = '%s%s%s%s%s%s%s' % (
                binascii.hexlify(struct.pack('>I', dbg.entry.Signature_Data1)).decode('ascii').upper(),
                binascii.hexlify(struct.pack('>H', dbg.entry.Signature_Data2)).decode('ascii').upper(),
                binascii.hexlify(struct.pack('>H', dbg.entry.Signature_Data3)).decode('ascii').upper(),
                binascii.hexlify(dbg.entry.Signature_Data4).decode('ascii').upper() if isinstance(dbg.entry.Signature_Data4, bytes) else struct.pack('H', dbg.entry.Signature_Data4).hex().upper(),
                binascii.hexlify(dbg.entry.Signature_Data5).decode('ascii').upper() if isinstance(dbg.entry.Signature_Data5, bytes) else struct.pack('H', dbg.entry.Signature_Data5).hex().upper(),
                binascii.hexlify(dbg.entry.Signature_Data6).decode('ascii').upper() if isinstance(dbg.entry.Signature_Data6, bytes) else struct.pack('I', dbg.entry.Signature_Data6).hex().upper(),
                dbg.entry.Age)

            break

    return 'http://msdl.microsoft.com/download/symbols/%s/%s/%s' % (pdb, guid, pdb)

def main():
    if len(sys.argv) < 2:
        print("Usage: %s /tmp/notepad.exe /tmp/kernel32.dll" % (sys.argv[0]))
        return

    for filename in sys.argv[1:]:
        downurl = build_url(filename)
        destfile = os.path.dirname(os.path.abspath(filename)) + "/" + to_pdb(filename)

        print("Saving %s to %s" % (downurl, destfile))
        os.system("curl -L %s -o %s" % (downurl, destfile))

if __name__ == '__main__':
    main()

Изменяем Imagebase на 0x0000000010000000 в IDA Pro.

29.png

Ставим breakpoint на начало функции BmMain.

30.png

Настраиваем отладку в IDA Pro и запускаем отладку.

31.png

В функции BmMain мы можем увидеть срабатывание breakpoint.

32.png


2.3 Переход из файла bootmgfw.elf в файл bootmgr.elf.​

Теперь нам необходимо перейти из файла bootmgfw.elf в файл bootmgr.elf, однако возникает сложность: непонятно, как это сделать. Не ясно, какие функции исследовать в файле bootmgfw.elf, а изучать и отлаживать всё подряд — неэффективно. В такой ситуации Google становится нашим лучшим помощником. Во время поиска информации было найдено несколько полезных проектов:

  1. ReactOS: boot/freeldr/freeldr/bootmgr.c File Reference: В этот проекте есть детальная документация по структурам, функциям, их аргументам и многому другому для ОС Windows.
  2. GitHub - backengineering/Voyager: A Hyper-V Hacking Framework For Windows 10 x64 (AMD & Intel): В этом проекте наибольшую ценность представляет фотография, на которой указаны функции BlImgLoadPEImageEx, BlImgLoadPEImage, BlImgAllocateImageBuffer, ImgArchStartBootApplication.

33.png

Эти находки значительно упрощают задачу и помогают понять с чего начать исследование.

Начнем с восстановления читабельности функции BlImgLoadPEImageEx и получим следующие. Замечание: В этих двух проектах есть описание функции BlImgLoadPEImageEx.

34.png

Поставим breakpoint на функцию BlImgLoadPEImageEx -> ImgpOpenFile , чтобы детально проанализировать передаваемые в неё аргументы.

35.png

Запускаем отладку в IDA Pro и останавливаемся на функции BlImgLoadPEImageEx -> ImgpOpenFile. Однако мы не можем просмотреть содержимое аргументов (регистров или памяти), потому что IDA Pro не может отобразить эту память — она находится вне области видимости IDA Pro.

36.png

Для решения этой проблемы напишем скрипт read_memory.py для IDA Pro.

Python:
import idc
import sys

def read_memory_debug(ea, size):
    memory_data = []
    for i in range(size):
        byte = idc.DbgByte(ea + i)
        if byte == 0xFF and not idc.isLoaded(ea + i):
            print("Не удалось прочитать байт по адресу 0x{0:X}".format(ea + i))
            break
        memory_data.append(byte)
    return memory_data

def main():
    if len(sys.argv) != 3:
        print("Использование: script.py <адрес> <размер>")
        return

    try:
        start_ea = int(sys.argv[1], 16)  # преобразование адреса из строки в число
        size = int(sys.argv[2])
    except ValueError:
        print("Ошибка: убедитесь, что адрес и размер введены корректно.")
        return

    # Чтение и вывод данных
    offset = 0
    for i in range(0, size, 16):
        start_ea_offset = start_ea + offset
        data = read_memory_debug(start_ea_offset, 16)
        if data:
            hex_data = " ".join("{:02X}".format(byte) for byte in data)
            str_data = "".join(chr(byte) if 32 <= byte <= 126 else '.' for byte in data)
            print("0x{0:X} | ".format(start_ea_offset) + "{0:<47} | ".format(hex_data) + "{0}".format(str_data))
        offset += 16

if __name__ == "__main__":
    main()

Воспользуемся скриптом read_memory.py. При первом срабатывании breakpoint(BlImgLoadPEImageEx → ImgpOpenFile) видим открытие файла BOOTX64.ELF.MUI. Поскольку этот файл нас не интересует, просто нажимаем F9 и продолжаем выполнение.

37.png

При втором срабатывании breakpoint(BlImgLoadPEImageEx → ImgpOpenFile) видим открытие файла bootmgr.elf, это тот файл который нам нужен.

38.png




Теперь перейдем к изучениюфункции BlImgLoadPEImageEx → ImgpLoadPEI mage, откроем ссылку. И после исследования кода по ссылке, мы приходим к выводу, что нам нужно получить адрес VirtualAddress.


39.png

В IDA Pro устанавливаем breakpoint на функцию RtlImageNtHeaderEx (BlImgLoadPEImageEx → ImgpLoadPEImage → RtlImageNtHeaderEx).

40.png

Запускаем отладку в IDA Pro, останавливаемся на функции BlImgLoadPEImageEx -> ImgpLoadPEImage -> RtlImageNtHeaderEx и мы видим начало файла bootmgr.elf, которое начинается с 'MZ'.

41.png

Дальше перейдём к изучению функции ImgArchStartBootApplication и восстановим её читабельность. В проекте ReactOS: boot/freeldr/freeldr/bootmgr.c File Reference описание данной функции не было найдено, однако в GitHub - backengineering/Voyager: A Hyper-V Hacking Framework For Windows 10 x64 (AMD & Intel) удалось обнаружить описание её аргументов. Это означает, что нам придётся провести отладку, чтобы разобраться в работе функции ImgArchStartBootApplication. После отладки можно сделать вывод, что функция ImgArchStartBootApplication работает следующим образом.

Сначала мы проходим по функциям.

42.png

В конце функции Archpx64TransferTo64BitApplicationAsm, после retfq необходимо еще немного отладить.

43.png

В итоге, это приведет нас к call rax, при вызове которого происходит передача управления на функцию BmMain в файле bootmgr.elf.

44.png


2.4 Переход из файла bootmgr.elf в файл hvloader.efi.​

После того как мы нашли Imagebase 0x0000000000613000 файла bootmgr.elf и понимания, что нужно отлаживать функцию Archpx64TransferTo64BitApplicationAsm. Переход из файла bootmgr.elf в файл hvloader.efi должен пройти без затруднений, так как принцип перехода такой же как переход из файла bootmgfw.elf в файл bootmgr.elf.

Откроем IDA Pro и загрузим файл bootmgr.elf (Файл bootmgr.elf берётся из E:\EFI\ minram\bootmgr.elf).

45.png

Добавляем bootmgr.pdb файл в IDA Pro (Ссылка для скачивания).

46.png

Изменяем Imagebase на 0x0000000000613000 в IDA Pro.

47.png

Устанавливаем breakpoint в конец функции Archpx64TransferTo64BitApplicationAsm на retfq.

48.png

Запускаем отладку в IDA Pro, останавливаемся в конец функции Archpx64TransferTo64BitApplicationAsm на retfq и после retfq еще немного отлаживаем.

49.png

И это нас привело к call rax, при вызове которого происходит передача управления на функцию HvlMain в файле hvloader.efi.

50.png
 
Последнее редактирование:
Теперь вычислим Imagebase файла hvloader.efi, для этого отнимем от 0x106C84B8 число 0x24B8 и получим Imagebase 0x106C6000.

Число 0x24B8 можно вычислить следующим образом:

• Загрузить файл hvloader.efi в IDA PRO по дефолту Imagebase будет 0x140000000.

51.png.​

• Перейти в функцию HvlMain расположенная по адресу 0x1400024B8.

52.png

• Отнять от 0x1400024B8 число 0x140000000 получим 0x24B8.

2.5 Переход из файла hvloader.efi в файл mcupdate_....dll.​

После того как мы нашли Imagebase 0x106C6000 файла hvloader.efi, перейдем к изучению перехода из файла hvloader.efi в файл mcupdate_....dll. Для этого обратимся к описанию файла hvloader.efi, найденному в интернете, и выделим одну важную функцию BtLoadUpdateDll, на которую следует обратить особое внимание. Данная функция загружает файл mcupdate_....dll. Теперь установим breakpoint на функцию BtLoadUpdateDll, чтобы убедиться, происходит ли переход в неё.

Откроем IDA Pro и загрузим файл hvloader.efi (Файл hvloader.efi берётся из E:\EFI\ maxram\hvloader.efi).

53.png

Добавляем hvloader.pdb файл в IDA Pro (Ссылка для скачивания).

54.png


Изменяем Imagebase на 0x106C6000 в IDA Pro.


55.png

Устанавливаем breakpoint на функции BtLoadUpdateDll.

56.png

Запускаем отладку в IDA Pro и breakpoint на функции BtLoadUpdateDll не срабатывает, тогда давайте установим breakpoint на test eax, eax(0x00000000106C8349).

57.png

Запускаем отладку в IDA Pro и breakpoint на test eax, eax срабатывает.

58.png

Теперь нам необходимо понять почему не происходит переход с 0x00000000106C834B на 0x00000000106C835D и начнем с разбора функции HvlpSLATPresent.

59.png

После поиска описания функции HvlpSLATPresent в интернете, можно сделать вывод, что SLAT - это технология виртуализации с аппаратным обеспечением, которая позволяет избежать накладных расходов, связанных с управляемыми программным обеспечением таблицами теневых страниц (https://ru.wikipedia.org/wiki/SLAT).

Мы можем предположить, что технология SLAT отключена у виртуальной машины Windows 10x64-BatonDrop и нам необходимо ее включить. По данной ссылке есть описание как это можно сделать. Сначала отключим виртуальную машину Windows 10x64-BatonDrop. Дальше включим SLAT, проделав следующие шаги и посмотрим, что произойдет.

60.png

Запускаем отладку в IDA Pro и breakpoint на функции BtLoadUpdateDll срабатывает.

61.png

Теперь просто нажмем F9 и посмотрим, что произойдет. На экране можно увидеть выполнение CVE-2022-21894 (baton drop).

62.png


Продолжим изучение кода, чтобы понять, где происходит переход из файла hvloader.efi в файл mcupdate_....dll.

Для этого изучим функцию BtLoadUpdateDll, на сайте есть описание данной функции BtLoadUpdateDll.

63.png

Нас интересуют строки 26 и 27, где указаны названия ImageBase и AddressOfEntryPoint файла mcupdate_....dll. Установим breakpoints на этих строках (В IDA Pro это строки 47 и 48).

64.png


Запускаем отладку в IDA Pro и в строчке 47 мы видим ImageBase файла mcupdate_....dll

65.png

А в строке 48 мы можем увидеть AddressOfEntryPoint файла mcupdate_....dll. На фото изображен ассемблерный код, так как hex-reys не отображает эту информацию в строке 48.

66.png

Теперь перейдем в функцию HvlpLoadMicrocode, так как в нее передается AddressOfEntryPoint (imageEP).

67.png

В функции HvlpLoadMicrocode по адресу 0x00000000106C8D42(call rax) происходит переход в файл mcupdate_....dll.

68.png


Но у нас не получилось отладить mcupdate_....dll, как мы отлаживали hvloader.efi, bootmgr.elf, bootmgfw.elf. Потому что у файла mcupdate_....dll есть ASLR, так как при каждом запуске виртуальной машины меняется ImageBase.


69.png

таком случае, что бы отладить mcupdate_....dll, нам нужно сделать следующие шаги.

Делаем snapshot до перехода в mcupdate_....dll.


70.png

Узнаем ImageBase 0xFFFFF800D914D000.

71.png

Проваливаемся в call rax в функции HvlpLoadMicrocode.

72.png

Делаем snapshot после того, как провалились в call rax в функции HvlpLoadMicrocode.

73.png

Дальше завершаем процесс в IDA Pro.

74.png


Теперь возвращаемся к snapshot.


75.png

Открываем mcupdate...dll в IDA Pro и изменяем ImageBase (В нашем случае он 0xFFFFF800D914D000).

76.png

Ставим breakpoint на функцию DriverEntry в файле mcupdate...dll и запускаем отладку в IDA Pro.

77.png
 
Сверху